opentool 0.4.6 → 0.6.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 (49) hide show
  1. package/README.md +95 -30
  2. package/dist/ai/index.d.ts +237 -0
  3. package/dist/ai/index.js +759 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/cli/index.d.ts +38 -5
  6. package/dist/cli/index.js +2218 -67
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/index-D3DaM5Rs.d.ts +1693 -0
  9. package/dist/index.d.ts +33 -5
  10. package/dist/index.js +3258 -25
  11. package/dist/index.js.map +1 -1
  12. package/dist/payment/index.d.ts +2 -0
  13. package/dist/payment/index.js +969 -0
  14. package/dist/payment/index.js.map +1 -0
  15. package/dist/validate-DiIOFUU5.d.ts +828 -0
  16. package/dist/wallets/index.d.ts +117 -0
  17. package/dist/wallets/index.js +337 -0
  18. package/dist/wallets/index.js.map +1 -0
  19. package/package.json +39 -4
  20. package/dist/cli/build.d.ts +0 -8
  21. package/dist/cli/build.d.ts.map +0 -1
  22. package/dist/cli/build.js +0 -508
  23. package/dist/cli/build.js.map +0 -1
  24. package/dist/cli/dev.d.ts +0 -6
  25. package/dist/cli/dev.d.ts.map +0 -1
  26. package/dist/cli/dev.js +0 -168
  27. package/dist/cli/dev.js.map +0 -1
  28. package/dist/cli/generate-metadata.d.ts +0 -15
  29. package/dist/cli/generate-metadata.d.ts.map +0 -1
  30. package/dist/cli/generate-metadata.js +0 -291
  31. package/dist/cli/generate-metadata.js.map +0 -1
  32. package/dist/cli/index.d.ts.map +0 -1
  33. package/dist/cli/validate.d.ts +0 -8
  34. package/dist/cli/validate.d.ts.map +0 -1
  35. package/dist/cli/validate.js +0 -328
  36. package/dist/cli/validate.js.map +0 -1
  37. package/dist/index.d.ts.map +0 -1
  38. package/dist/runtime/index.d.ts +0 -11
  39. package/dist/runtime/index.d.ts.map +0 -1
  40. package/dist/runtime/index.js +0 -192
  41. package/dist/runtime/index.js.map +0 -1
  42. package/dist/types/index.d.ts +0 -50
  43. package/dist/types/index.d.ts.map +0 -1
  44. package/dist/types/index.js +0 -3
  45. package/dist/types/index.js.map +0 -1
  46. package/dist/types/metadata.d.ts +0 -65
  47. package/dist/types/metadata.d.ts.map +0 -1
  48. package/dist/types/metadata.js +0 -3
  49. package/dist/types/metadata.js.map +0 -1
package/dist/cli/index.js CHANGED
@@ -1,70 +1,2221 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
2
+ import { program } from 'commander';
3
+ import * as fs4 from 'fs';
4
+ import * as path5 from 'path';
5
+ import { tmpdir } from 'os';
6
+ import { build } from 'esbuild';
7
+ import { z } from 'zod';
8
+ import { createRequire } from 'module';
9
+ import { fileURLToPath, pathToFileURL } from 'url';
10
+ import { zodToJsonSchema } from 'zod-to-json-schema';
11
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
+ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
14
+ import * as http from 'http';
15
+
16
+ function resolveTsconfig(projectRoot) {
17
+ const candidate = path5.join(projectRoot, "tsconfig.json");
18
+ if (fs4.existsSync(candidate)) {
19
+ return candidate;
20
+ }
21
+ return void 0;
22
+ }
23
+ async function transpileWithEsbuild(options) {
24
+ if (options.entryPoints.length === 0) {
25
+ throw new Error("No entry points provided for esbuild transpilation");
26
+ }
27
+ const projectRoot = options.projectRoot;
28
+ const tempBase = options.outDir ?? fs4.mkdtempSync(path5.join(tmpdir(), "opentool-"));
29
+ if (!fs4.existsSync(tempBase)) {
30
+ fs4.mkdirSync(tempBase, { recursive: true });
31
+ }
32
+ const tsconfig = resolveTsconfig(projectRoot);
33
+ const buildOptions = {
34
+ entryPoints: options.entryPoints,
35
+ outdir: tempBase,
36
+ bundle: false,
37
+ format: options.format,
38
+ platform: "node",
39
+ target: "node20",
40
+ logLevel: "warning",
41
+ sourcesContent: false,
42
+ sourcemap: false,
43
+ packages: "external",
44
+ loader: {
45
+ ".ts": "ts",
46
+ ".tsx": "tsx",
47
+ ".cts": "ts",
48
+ ".mts": "ts",
49
+ ".js": "js",
50
+ ".jsx": "jsx",
51
+ ".mjs": "js",
52
+ ".cjs": "js"
53
+ },
54
+ metafile: false,
55
+ allowOverwrite: true,
56
+ absWorkingDir: projectRoot
57
+ };
58
+ if (tsconfig) {
59
+ buildOptions.tsconfig = tsconfig;
60
+ }
61
+ await build(buildOptions);
62
+ if (options.format === "esm") {
63
+ const packageJsonPath = path5.join(tempBase, "package.json");
64
+ if (!fs4.existsSync(packageJsonPath)) {
65
+ fs4.writeFileSync(packageJsonPath, JSON.stringify({ type: "module" }), "utf8");
66
+ }
67
+ }
68
+ const cleanup = () => {
69
+ if (options.outDir) {
70
+ return;
71
+ }
72
+ fs4.rmSync(tempBase, { recursive: true, force: true });
73
+ };
74
+ return { outDir: tempBase, cleanup };
75
+ }
76
+ var METADATA_SPEC_VERSION = "1.0.0";
77
+ var McpAnnotationsSchema = z.object({
78
+ title: z.string().optional(),
79
+ readOnlyHint: z.boolean().optional(),
80
+ destructiveHint: z.boolean().optional(),
81
+ idempotentHint: z.boolean().optional(),
82
+ openWorldHint: z.boolean().optional(),
83
+ requiresPayment: z.boolean().optional()
84
+ }).strict();
85
+ var PaymentConfigSchema = z.object({
86
+ amountUSDC: z.number().nonnegative().optional(),
87
+ description: z.string().optional(),
88
+ x402: z.boolean().optional(),
89
+ plain402: z.boolean().optional(),
90
+ acceptedMethods: z.array(z.union([z.literal("x402"), z.literal("402")])).optional(),
91
+ acceptedCurrencies: z.array(z.string()).optional(),
92
+ chainIds: z.array(z.number().int()).optional(),
93
+ facilitator: z.string().optional()
94
+ }).strict();
95
+ var DiscoveryMetadataSchema = z.object({
96
+ keywords: z.array(z.string()).optional(),
97
+ category: z.string().optional(),
98
+ useCases: z.array(z.string()).optional(),
99
+ capabilities: z.array(z.string()).optional(),
100
+ requirements: z.record(z.any()).optional(),
101
+ pricing: z.record(z.any()).optional(),
102
+ compatibility: z.record(z.any()).optional(),
103
+ documentation: z.union([z.string(), z.array(z.string())]).optional()
104
+ }).catchall(z.any());
105
+ var ToolMetadataOverridesSchema = z.object({
106
+ name: z.string().optional(),
107
+ description: z.string().optional(),
108
+ annotations: McpAnnotationsSchema.optional(),
109
+ payment: PaymentConfigSchema.optional(),
110
+ discovery: DiscoveryMetadataSchema.optional()
111
+ }).catchall(z.any());
112
+ var ToolSchema = z.object({
113
+ name: z.string(),
114
+ description: z.string(),
115
+ inputSchema: z.any(),
116
+ annotations: McpAnnotationsSchema.optional(),
117
+ payment: PaymentConfigSchema.optional(),
118
+ discovery: DiscoveryMetadataSchema.optional()
119
+ }).strict();
120
+ var AuthoredMetadataSchema = z.object({
121
+ metadataSpecVersion: z.string().optional(),
122
+ name: z.string().optional(),
123
+ displayName: z.string().optional(),
124
+ version: z.string().optional(),
125
+ description: z.string().optional(),
126
+ author: z.string().optional(),
127
+ repository: z.string().optional(),
128
+ website: z.string().optional(),
129
+ category: z.string().optional(),
130
+ categories: z.array(z.string()).optional(),
131
+ termsOfService: z.string().optional(),
132
+ mcpUrl: z.string().optional(),
133
+ payment: PaymentConfigSchema.optional(),
134
+ discovery: DiscoveryMetadataSchema.optional(),
135
+ promptExamples: z.array(z.string()).optional(),
136
+ iconPath: z.string().optional(),
137
+ videoPath: z.string().optional(),
138
+ image: z.string().optional(),
139
+ animation_url: z.string().optional(),
140
+ keywords: z.array(z.string()).optional(),
141
+ useCases: z.array(z.string()).optional(),
142
+ capabilities: z.array(z.string()).optional(),
143
+ requirements: z.record(z.any()).optional(),
144
+ pricing: z.record(z.any()).optional(),
145
+ compatibility: z.record(z.any()).optional()
146
+ }).catchall(z.any());
147
+ var MetadataSchema = z.object({
148
+ metadataSpecVersion: z.string().default(METADATA_SPEC_VERSION),
149
+ name: z.string(),
150
+ displayName: z.string(),
151
+ version: z.string(),
152
+ description: z.string().optional(),
153
+ author: z.string().optional(),
154
+ repository: z.string().optional(),
155
+ website: z.string().optional(),
156
+ category: z.string(),
157
+ termsOfService: z.string().optional(),
158
+ mcpUrl: z.string().optional(),
159
+ payment: PaymentConfigSchema.optional(),
160
+ tools: z.array(ToolSchema).min(1),
161
+ discovery: DiscoveryMetadataSchema.optional(),
162
+ promptExamples: z.array(z.string()).optional(),
163
+ iconPath: z.string().optional(),
164
+ videoPath: z.string().optional(),
165
+ image: z.string().optional(),
166
+ animation_url: z.string().optional()
167
+ }).strict();
168
+ createRequire(import.meta.url);
169
+ function resolveCompiledPath(outDir, originalFile, extension = ".js") {
170
+ const baseName = path5.basename(originalFile).replace(/\.[^.]+$/, "");
171
+ return path5.join(outDir, `${baseName}${extension}`);
172
+ }
173
+ async function importFresh(modulePath) {
174
+ const fileUrl = pathToFileURL(modulePath).href;
175
+ const cacheBuster = `t=${Date.now()}-${Math.random()}`;
176
+ const separator = fileUrl.includes("?") ? "&" : "?";
177
+ return import(`${fileUrl}${separator}${cacheBuster}`);
178
+ }
179
+
180
+ // src/cli/shared/metadata.ts
181
+ var METADATA_ENTRY = "metadata.ts";
182
+ async function loadAuthoredMetadata(projectRoot) {
183
+ const absPath = path5.join(projectRoot, METADATA_ENTRY);
184
+ if (!fs4.existsSync(absPath)) {
185
+ throw new Error(
186
+ `metadata.ts not found in ${projectRoot}. Create metadata.ts to describe your agent.`
187
+ );
188
+ }
189
+ const tempDir = path5.join(projectRoot, ".opentool-temp");
190
+ if (fs4.existsSync(tempDir)) {
191
+ fs4.rmSync(tempDir, { recursive: true, force: true });
192
+ }
193
+ const { outDir, cleanup } = await transpileWithEsbuild({
194
+ entryPoints: [absPath],
195
+ projectRoot,
196
+ format: "esm",
197
+ outDir: tempDir
198
+ });
199
+ try {
200
+ const compiledPath = resolveCompiledPath(outDir, METADATA_ENTRY);
201
+ const moduleExports = await importFresh(compiledPath);
202
+ const metadataExport = extractMetadataExport(moduleExports);
203
+ const parsed = AuthoredMetadataSchema.parse(metadataExport);
204
+ return { metadata: parsed, sourcePath: absPath };
205
+ } finally {
206
+ cleanup();
207
+ if (fs4.existsSync(tempDir)) {
208
+ fs4.rmSync(tempDir, { recursive: true, force: true });
209
+ }
210
+ }
211
+ }
212
+ function extractMetadataExport(moduleExports) {
213
+ if (!moduleExports || typeof moduleExports !== "object") {
214
+ throw new Error("metadata.ts must export a metadata object");
215
+ }
216
+ const exportsObject = moduleExports;
217
+ if (exportsObject.metadata) {
218
+ return exportsObject.metadata;
219
+ }
220
+ if (exportsObject.default && typeof exportsObject.default === "object") {
221
+ const defaultExport = exportsObject.default;
222
+ if (defaultExport.metadata) {
223
+ return defaultExport.metadata;
224
+ }
225
+ return defaultExport;
226
+ }
227
+ return moduleExports;
228
+ }
229
+ function readPackageJson(projectRoot) {
230
+ const packagePath = path5.join(projectRoot, "package.json");
231
+ if (!fs4.existsSync(packagePath)) {
232
+ return {};
233
+ }
234
+ try {
235
+ const content = fs4.readFileSync(packagePath, "utf8");
236
+ return JSON.parse(content);
237
+ } catch (error) {
238
+ throw new Error(`Failed to read package.json: ${error}`);
239
+ }
240
+ }
241
+ async function buildMetadataArtifact(options) {
242
+ const projectRoot = options.projectRoot;
243
+ const packageInfo = readPackageJson(projectRoot);
244
+ const { metadata: authored, sourcePath } = await loadAuthoredMetadata(projectRoot);
245
+ const defaultsApplied = [];
246
+ const folderName = path5.basename(projectRoot);
247
+ const name = resolveField(
248
+ "name",
249
+ authored.name,
250
+ () => packageInfo.name ?? folderName,
251
+ defaultsApplied,
252
+ "package.json name"
253
+ );
254
+ const displayName = resolveField(
255
+ "displayName",
256
+ authored.displayName,
257
+ () => {
258
+ const source = packageInfo.name ?? folderName;
259
+ return source.split(/[-_]/).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
260
+ },
261
+ defaultsApplied,
262
+ "package.json name"
263
+ );
264
+ const versionRaw = resolveField(
265
+ "version",
266
+ authored.version,
267
+ () => packageInfo.version ?? "0.1.0",
268
+ defaultsApplied,
269
+ "package.json version"
270
+ );
271
+ const version = typeof versionRaw === "number" ? String(versionRaw) : versionRaw;
272
+ const category = determineCategory(authored, defaultsApplied);
273
+ const description = authored.description ?? packageInfo.description;
274
+ if (!authored.description && packageInfo.description) {
275
+ defaultsApplied.push("description \u2192 package.json description");
276
+ }
277
+ const author = authored.author ?? packageInfo.author;
278
+ if (!authored.author && packageInfo.author) {
279
+ defaultsApplied.push("author \u2192 package.json author");
280
+ }
281
+ const repository = authored.repository ?? extractRepository(packageInfo.repository);
282
+ if (!authored.repository && repository) {
283
+ defaultsApplied.push("repository \u2192 package.json repository");
284
+ }
285
+ const website = authored.website ?? packageInfo.homepage;
286
+ if (!authored.website && packageInfo.homepage) {
287
+ defaultsApplied.push("website \u2192 package.json homepage");
288
+ }
289
+ const payment = resolvePayment(authored, defaultsApplied);
290
+ const baseImage = authored.image ?? authored.iconPath;
291
+ const animation = authored.animation_url ?? authored.videoPath;
292
+ const discovery = buildDiscovery(authored);
293
+ const metadataTools = options.tools.map((tool) => {
294
+ const overrides = tool.metadata ? ToolMetadataOverridesSchema.parse(tool.metadata) : {};
295
+ const toolName = overrides.name ?? tool.filename;
296
+ const toolDescription = overrides.description ?? `${toolName} tool`;
297
+ const toolPayment = overrides.payment ?? payment ?? void 0;
298
+ if (!overrides.payment && toolPayment && payment && toolPayment === payment) {
299
+ defaultsApplied.push(`tool ${toolName} payment \u2192 agent payment`);
300
+ }
301
+ const toolDiscovery = overrides.discovery ?? void 0;
302
+ const toolDefinition = {
303
+ name: toolName,
304
+ description: toolDescription,
305
+ inputSchema: tool.inputSchema
306
+ };
307
+ if (overrides.annotations) {
308
+ toolDefinition.annotations = overrides.annotations;
309
+ }
310
+ if (toolPayment) {
311
+ toolDefinition.payment = toolPayment;
312
+ }
313
+ if (toolDiscovery) {
314
+ toolDefinition.discovery = toolDiscovery;
315
+ }
316
+ return toolDefinition;
317
+ });
318
+ const metadata = MetadataSchema.parse({
319
+ metadataSpecVersion: authored.metadataSpecVersion ?? METADATA_SPEC_VERSION,
320
+ name,
321
+ displayName,
322
+ version,
323
+ description,
324
+ author,
325
+ repository,
326
+ website,
327
+ category,
328
+ termsOfService: authored.termsOfService,
329
+ mcpUrl: authored.mcpUrl,
330
+ payment: payment ?? void 0,
331
+ tools: metadataTools,
332
+ discovery,
333
+ promptExamples: authored.promptExamples,
334
+ iconPath: authored.iconPath,
335
+ videoPath: authored.videoPath,
336
+ image: baseImage,
337
+ animation_url: animation
338
+ });
339
+ return {
340
+ metadata,
341
+ defaultsApplied,
342
+ sourceMetadataPath: sourcePath
343
+ };
344
+ }
345
+ function resolveField(field, value, fallback, defaultsApplied, fallbackLabel) {
346
+ if (value !== void 0 && value !== null && value !== "") {
347
+ return value;
348
+ }
349
+ const resolved = fallback();
350
+ defaultsApplied.push(`${field} \u2192 ${fallbackLabel}`);
351
+ return resolved;
352
+ }
353
+ function determineCategory(authored, defaultsApplied) {
354
+ if (authored.category) {
355
+ return authored.category;
356
+ }
357
+ if (Array.isArray(authored.categories) && authored.categories.length > 0) {
358
+ defaultsApplied.push("category \u2192 metadata.categories[0]");
359
+ return authored.categories[0];
360
+ }
361
+ defaultsApplied.push("category \u2192 default category");
362
+ return "utility";
363
+ }
364
+ function extractRepository(repository) {
365
+ if (!repository) {
366
+ return void 0;
367
+ }
368
+ if (typeof repository === "string") {
369
+ return repository;
370
+ }
371
+ return repository.url;
372
+ }
373
+ function resolvePayment(authored, defaults) {
374
+ if (authored.payment) {
375
+ return authored.payment;
376
+ }
377
+ const discoveryPricing = authored.discovery?.pricing;
378
+ const legacyPricing = authored.pricing;
379
+ const pricing = discoveryPricing ?? legacyPricing;
380
+ if (!pricing) {
381
+ return void 0;
382
+ }
383
+ const amount = typeof pricing.defaultAmount === "number" ? pricing.defaultAmount : 0;
384
+ const sourceLabel = discoveryPricing ? "discovery.pricing.defaultAmount" : "pricing.defaultAmount";
385
+ defaults.push(`payment \u2192 synthesized from ${sourceLabel}`);
386
+ const acceptedMethodsRaw = Array.isArray(pricing.acceptedMethods) ? pricing.acceptedMethods : ["402"];
387
+ const acceptedMethods = acceptedMethodsRaw.map(
388
+ (method) => method === "x402" ? "x402" : "402"
389
+ );
390
+ return {
391
+ amountUSDC: amount,
392
+ description: typeof pricing.description === "string" ? pricing.description : void 0,
393
+ x402: acceptedMethods.includes("x402"),
394
+ plain402: acceptedMethods.includes("402"),
395
+ acceptedMethods,
396
+ acceptedCurrencies: Array.isArray(pricing.acceptedCurrencies) ? pricing.acceptedCurrencies : ["USDC"],
397
+ chainIds: Array.isArray(pricing.chainIds) ? pricing.chainIds : [8453]
398
+ };
399
+ }
400
+ function buildDiscovery(authored) {
401
+ const legacyDiscovery = {};
402
+ if (Array.isArray(authored.keywords) && authored.keywords.length > 0) {
403
+ legacyDiscovery.keywords = authored.keywords;
404
+ }
405
+ if (Array.isArray(authored.useCases) && authored.useCases.length > 0) {
406
+ legacyDiscovery.useCases = authored.useCases;
407
+ }
408
+ if (Array.isArray(authored.capabilities) && authored.capabilities.length > 0) {
409
+ legacyDiscovery.capabilities = authored.capabilities;
410
+ }
411
+ if (authored.requirements) {
412
+ legacyDiscovery.requirements = authored.requirements;
413
+ }
414
+ if (authored.compatibility) {
415
+ legacyDiscovery.compatibility = authored.compatibility;
416
+ }
417
+ if (Array.isArray(authored.categories) && authored.categories.length > 0) {
418
+ legacyDiscovery.category = authored.categories[0];
419
+ }
420
+ if (authored.pricing) {
421
+ legacyDiscovery.pricing = authored.pricing;
422
+ }
423
+ const merged = {
424
+ ...legacyDiscovery,
425
+ ...authored.discovery ?? {}
426
+ };
427
+ return Object.keys(merged).length > 0 ? merged : void 0;
428
+ }
429
+ var PAYMENT_SCHEMA_VERSION = 1;
430
+ var paymentSchemaVersionSchema = z.literal(PAYMENT_SCHEMA_VERSION);
431
+ var decimalStringSchema = z.string().regex(/^(?:0|[1-9]\d*)(?:\.\d+)?$/, "Value must be a positive decimal string");
432
+ var currencySchema = z.object({
433
+ code: z.string().min(2).max(12).transform((value) => value.toUpperCase()),
434
+ symbol: z.string().min(1).max(6).optional(),
435
+ decimals: z.number().int().min(0).max(36).optional(),
436
+ kind: z.enum(["fiat", "crypto"]).default("crypto").optional(),
437
+ description: z.string().optional()
438
+ });
439
+ var paymentAmountSchema = z.object({
440
+ value: decimalStringSchema,
441
+ currency: currencySchema,
442
+ display: z.string().optional()
443
+ });
444
+ var cryptoAssetSchema = z.object({
445
+ symbol: z.string().min(2).max(12),
446
+ network: z.string().min(1).optional(),
447
+ chainId: z.number().int().min(0).optional(),
448
+ address: z.string().min(1).optional(),
449
+ decimals: z.number().int().min(0).max(36).optional(),
450
+ standard: z.enum(["erc20", "spl", "custom"]).default("erc20").optional(),
451
+ description: z.string().optional()
452
+ });
453
+ var facilitatorConfigSchema = z.object({
454
+ url: z.string().url(),
455
+ vendor: z.string().optional(),
456
+ verifyPath: z.string().default("/verify").optional(),
457
+ settlePath: z.string().default("/settle").optional(),
458
+ apiKey: z.string().optional(),
459
+ apiKeyEnv: z.string().optional(),
460
+ apiKeyHeader: z.string().default("Authorization").optional(),
461
+ headers: z.record(z.string(), z.string()).optional(),
462
+ timeoutMs: z.number().int().positive().optional()
463
+ });
464
+ var settlementTermsSchema = z.object({
465
+ windowSeconds: z.number().int().positive().optional(),
466
+ targetConfirmations: z.number().int().positive().optional(),
467
+ finalityDescription: z.string().optional(),
468
+ slaDescription: z.string().optional()
469
+ });
470
+ var paymentFieldSchema = z.object({
471
+ key: z.string().min(1),
472
+ label: z.string().min(1),
473
+ required: z.boolean().default(true).optional(),
474
+ description: z.string().optional(),
475
+ example: z.string().optional()
476
+ });
477
+ var x402ProofSchema = z.object({
478
+ mode: z.literal("x402"),
479
+ scheme: z.string().min(1),
480
+ network: z.string().min(1),
481
+ version: z.number().int().min(1).optional(),
482
+ facilitator: facilitatorConfigSchema.optional(),
483
+ verifier: z.string().optional()
484
+ });
485
+ var directProofSchema = z.object({
486
+ mode: z.literal("direct"),
487
+ proofTypes: z.array(z.string().min(1)).nonempty(),
488
+ verifier: z.string().optional(),
489
+ instructions: z.string().optional(),
490
+ fields: z.array(paymentFieldSchema).optional(),
491
+ allowsManualReview: z.boolean().optional()
492
+ });
493
+ var paymentProofSchema = z.discriminatedUnion("mode", [
494
+ x402ProofSchema,
495
+ directProofSchema
496
+ ]);
497
+ var paymentOptionSchema = z.object({
498
+ id: z.string().min(1),
499
+ title: z.string().min(1),
500
+ description: z.string().optional(),
501
+ amount: paymentAmountSchema,
502
+ asset: cryptoAssetSchema,
503
+ payTo: z.string().min(1),
504
+ resource: z.string().url().optional(),
505
+ proof: paymentProofSchema,
506
+ settlement: settlementTermsSchema.optional(),
507
+ metadata: z.record(z.string(), z.unknown()).optional()
508
+ });
509
+ var paymentRequirementsSchema = z.object({
510
+ schemaVersion: paymentSchemaVersionSchema,
511
+ message: z.string().optional(),
512
+ title: z.string().optional(),
513
+ resource: z.string().url().optional(),
514
+ accepts: z.array(paymentOptionSchema).nonempty(),
515
+ metadata: z.record(z.string(), z.unknown()).optional(),
516
+ fallbackText: z.string().optional()
517
+ });
518
+ var x402PaymentHeaderSchema = z.object({
519
+ x402Version: z.number().int().min(1),
520
+ scheme: z.string().min(1),
521
+ network: z.string().min(1),
522
+ payload: z.unknown(),
523
+ correlationId: z.string().optional()
524
+ });
525
+ var directPaymentPayloadSchema = z.object({
526
+ schemaVersion: z.literal(1),
527
+ optionId: z.string().min(1),
528
+ proofType: z.string().min(1),
529
+ payload: z.unknown(),
530
+ metadata: z.record(z.string(), z.unknown()).optional()
531
+ });
532
+ var paymentSuccessMetadataSchema = z.object({
533
+ optionId: z.string().min(1),
534
+ verifier: z.string().optional(),
535
+ txHash: z.string().optional(),
536
+ networkId: z.string().optional(),
537
+ amount: paymentAmountSchema.optional(),
538
+ settledAt: z.string().datetime().optional(),
539
+ payload: z.unknown().optional()
540
+ });
541
+ var paymentFailureSchema = z.object({
542
+ reason: z.string().min(1),
543
+ code: z.enum([
544
+ "verifier_not_found",
545
+ "verification_failed",
546
+ "invalid_payload",
547
+ "unsupported_option",
548
+ "missing_header",
549
+ "unknown"
550
+ ]).default("unknown").optional(),
551
+ retryable: z.boolean().optional(),
552
+ detail: z.unknown().optional()
553
+ });
554
+
555
+ // src/helpers/payment.ts
556
+ var X402_VERSION_DEFAULT = 1;
557
+ var HEADER_X402 = "X-PAYMENT";
558
+ var HEADER_DIRECT = "X-PAYMENT-PROOF";
559
+ var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
560
+ var x402RequirementSchema = z.object({
561
+ scheme: z.string().min(1),
562
+ network: z.string().min(1),
563
+ maxAmountRequired: z.string().min(1),
564
+ asset: z.string().min(1),
565
+ payTo: z.string().min(1),
566
+ resource: z.string().optional(),
567
+ description: z.string().optional(),
568
+ mimeType: z.string().optional(),
569
+ outputSchema: z.unknown().optional(),
570
+ maxTimeoutSeconds: z.number().int().positive().optional(),
571
+ extra: z.record(z.string(), z.unknown()).nullable().optional()
572
+ });
573
+ function createPaymentRequiredBody(definition) {
574
+ const parsed = paymentRequirementsSchema.parse(definition);
575
+ const x402Accepts = parsed.accepts.filter((option) => option.proof.mode === "x402").map(
576
+ (option) => toX402Requirement(option, parsed.resource, option.settlement)
577
+ ).filter((value) => Boolean(value));
578
+ const x402Body = x402Accepts.length > 0 ? {
579
+ x402Version: resolveX402Version(parsed.accepts),
580
+ error: parsed.message ?? "Payment required",
581
+ accepts: x402Accepts
582
+ } : void 0;
583
+ if (x402Body) {
584
+ return {
585
+ ...parsed,
586
+ x402: x402Body
587
+ };
588
+ }
589
+ return parsed;
590
+ }
591
+ function paymentRequiredResponse(definition, init) {
592
+ const body = createPaymentRequiredBody(definition);
593
+ const headers = new Headers(init?.headers);
594
+ if (!headers.has("content-type")) {
595
+ headers.set("content-type", "application/json; charset=utf-8");
596
+ }
597
+ return new Response(JSON.stringify(body), {
598
+ ...init,
599
+ status: 402,
600
+ headers
601
+ });
602
+ }
603
+ function extractPaymentAttempts(source) {
604
+ const attempts = [];
605
+ const failures = [];
606
+ const x402Header = source.headers.get(HEADER_X402);
607
+ if (x402Header) {
608
+ const { attempt, failure } = parseX402Header(x402Header);
609
+ if (attempt) {
610
+ attempts.push(attempt);
611
+ } else if (failure) {
612
+ failures.push(failure);
613
+ }
614
+ }
615
+ const directHeader = source.headers.get(HEADER_DIRECT);
616
+ if (directHeader) {
617
+ const { attempt, failure } = parseDirectHeader(directHeader);
618
+ if (attempt) {
619
+ attempts.push(attempt);
620
+ } else if (failure) {
621
+ failures.push(failure);
622
+ }
623
+ }
624
+ if (attempts.length === 0 && failures.length === 0) {
625
+ failures.push(
626
+ paymentFailureSchema.parse({
627
+ reason: "No payment headers present",
628
+ code: "missing_header",
629
+ retryable: false
630
+ })
631
+ );
632
+ }
633
+ return { attempts, failures };
634
+ }
635
+ async function verifyPayment(options) {
636
+ const definition = paymentRequirementsSchema.parse(options.definition);
637
+ const attempts = options.attempts ? options.attempts : options.request ? extractPaymentAttempts(options.request).attempts : [];
638
+ if (attempts.length === 0) {
639
+ return {
640
+ success: false,
641
+ optionId: "",
642
+ attemptType: "direct",
643
+ failure: paymentFailureSchema.parse({
644
+ reason: "No payment attempt found",
645
+ code: "missing_header",
646
+ retryable: false
647
+ })
648
+ };
649
+ }
650
+ for (const attempt of attempts) {
651
+ const option = findMatchingOption(definition, attempt);
652
+ if (!option) {
653
+ continue;
654
+ }
655
+ if (attempt.type === "x402") {
656
+ const proof = option.proof;
657
+ const verifierId = proof.verifier ?? (proof.facilitator ? "x402:facilitator" : void 0);
658
+ if (verifierId === "x402:facilitator" && proof.facilitator) {
659
+ const context2 = {
660
+ attempt,
661
+ option,
662
+ definition
663
+ };
664
+ if (options.settle !== void 0) {
665
+ context2.settle = options.settle;
666
+ }
667
+ return runFacilitatorVerifier({
668
+ ...context2,
669
+ fetchImpl: options.fetchImpl ?? fetch
670
+ });
671
+ }
672
+ const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
673
+ if (!verifier) {
674
+ return {
675
+ success: false,
676
+ optionId: option.id,
677
+ attemptType: attempt.type,
678
+ failure: paymentFailureSchema.parse({
679
+ reason: `No verifier registered for id: ${verifierId ?? "(missing)"}`,
680
+ code: "verifier_not_found",
681
+ retryable: false
682
+ })
683
+ };
684
+ }
685
+ const context = {
686
+ attempt,
687
+ option,
688
+ definition
689
+ };
690
+ if (options.settle !== void 0) {
691
+ context.settle = options.settle;
692
+ }
693
+ return verifier(context);
694
+ }
695
+ if (attempt.type === "direct") {
696
+ const proof = option.proof;
697
+ const verifierId = proof.verifier ?? `direct:${attempt.payload.proofType}`;
698
+ const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
699
+ if (!verifier) {
700
+ return {
701
+ success: false,
702
+ optionId: option.id,
703
+ attemptType: attempt.type,
704
+ failure: paymentFailureSchema.parse({
705
+ reason: `No verifier registered for id: ${verifierId}`,
706
+ code: "verifier_not_found",
707
+ retryable: false
708
+ })
709
+ };
710
+ }
711
+ const context = {
712
+ attempt,
713
+ option,
714
+ definition
715
+ };
716
+ if (options.settle !== void 0) {
717
+ context.settle = options.settle;
718
+ }
719
+ return verifier(context);
720
+ }
721
+ }
722
+ return {
723
+ success: false,
724
+ optionId: "",
725
+ attemptType: attempts[0]?.type ?? "direct",
726
+ failure: paymentFailureSchema.parse({
727
+ reason: "No matching payment option for attempt",
728
+ code: "unsupported_option",
729
+ retryable: false
730
+ })
731
+ };
732
+ }
733
+ function createPaymentResponseHeader(metadata) {
734
+ const parsed = paymentSuccessMetadataSchema.parse(metadata);
735
+ return encodeJson(parsed);
736
+ }
737
+ function parseX402Header(value) {
738
+ try {
739
+ const payload = decodeJson(value, x402PaymentHeaderSchema);
740
+ return {
741
+ attempt: {
742
+ type: "x402",
743
+ headerName: HEADER_X402,
744
+ raw: value,
745
+ payload
746
+ }
747
+ };
748
+ } catch (error) {
749
+ return {
750
+ failure: paymentFailureSchema.parse({
751
+ reason: `Invalid X-PAYMENT header: ${error.message}`,
752
+ code: "invalid_payload",
753
+ retryable: false
754
+ })
755
+ };
756
+ }
757
+ }
758
+ function parseDirectHeader(value) {
759
+ try {
760
+ const payload = decodeJson(value, directPaymentPayloadSchema);
761
+ return {
762
+ attempt: {
763
+ type: "direct",
764
+ headerName: HEADER_DIRECT,
765
+ raw: value,
766
+ payload
767
+ }
768
+ };
769
+ } catch (error) {
770
+ return {
771
+ failure: paymentFailureSchema.parse({
772
+ reason: `Invalid X-PAYMENT-PROOF header: ${error.message}`,
773
+ code: "invalid_payload",
774
+ retryable: false
775
+ })
776
+ };
777
+ }
778
+ }
779
+ function findMatchingOption(definition, attempt) {
780
+ return definition.accepts.find((candidate) => {
781
+ const option = paymentOptionSchema.parse(candidate);
782
+ if (attempt.type === "x402" && option.proof.mode === "x402") {
783
+ return option.proof.scheme === attempt.payload.scheme && option.proof.network === attempt.payload.network;
784
+ }
785
+ if (attempt.type === "direct" && option.proof.mode === "direct") {
786
+ return option.id === attempt.payload.optionId;
787
+ }
788
+ return false;
789
+ });
790
+ }
791
+ function resolveX402Version(options) {
792
+ const versions = [];
793
+ for (const option of options) {
794
+ if (option.proof.mode === "x402" && option.proof.version) {
795
+ versions.push(option.proof.version);
796
+ }
797
+ }
798
+ return versions.length > 0 ? Math.max(...versions) : X402_VERSION_DEFAULT;
799
+ }
800
+ function toX402Requirement(option, fallbackResource, settlement) {
801
+ if (option.proof.mode !== "x402") {
802
+ return void 0;
803
+ }
804
+ const decimals = resolveDecimals(option);
805
+ const assetAddress = option.asset.address;
806
+ if (!assetAddress) {
807
+ throw new Error(
808
+ `x402 payment option '${option.id}' is missing asset.address`
809
+ );
810
+ }
811
+ const units = decimalToBaseUnits(option.amount.value, decimals);
812
+ return x402RequirementSchema.parse({
813
+ scheme: option.proof.scheme,
814
+ network: option.proof.network,
815
+ maxAmountRequired: units,
816
+ asset: assetAddress,
817
+ payTo: option.payTo,
818
+ resource: option.resource ?? fallbackResource,
819
+ description: option.description,
820
+ maxTimeoutSeconds: settlement?.windowSeconds,
821
+ extra: {
822
+ symbol: option.asset.symbol,
823
+ currencyCode: option.amount.currency.code,
824
+ decimals
825
+ }
826
+ });
827
+ }
828
+ function resolveDecimals(option) {
829
+ if (typeof option.asset.decimals === "number") {
830
+ return option.asset.decimals;
831
+ }
832
+ if (typeof option.amount.currency.decimals === "number") {
833
+ return option.amount.currency.decimals;
834
+ }
835
+ throw new Error(
836
+ `Payment option '${option.id}' must specify asset.decimals or currency.decimals`
837
+ );
838
+ }
839
+ function decimalToBaseUnits(value, decimals) {
840
+ const [whole, fraction = ""] = value.split(".");
841
+ const sanitizedFraction = fraction.slice(0, decimals);
842
+ const paddedFraction = sanitizedFraction.padEnd(decimals, "0");
843
+ const combined = `${whole}${paddedFraction}`.replace(/^0+/, "");
844
+ return combined.length > 0 ? combined : "0";
845
+ }
846
+ function decodeJson(value, schema) {
847
+ const base64 = normalizeBase64(value);
848
+ const json = Buffer.from(base64, "base64").toString("utf-8");
849
+ const parsed = JSON.parse(json);
850
+ return schema.parse(parsed);
851
+ }
852
+ function encodeJson(value) {
853
+ const json = JSON.stringify(value);
854
+ return Buffer.from(json, "utf-8").toString("base64");
855
+ }
856
+ function normalizeBase64(input) {
857
+ if (/^[A-Za-z0-9+/=]+$/.test(input)) {
858
+ return input;
859
+ }
860
+ const restored = input.replace(/-/g, "+").replace(/_/g, "/");
861
+ const paddingNeeded = (4 - restored.length % 4) % 4;
862
+ return restored + "=".repeat(paddingNeeded);
863
+ }
864
+ async function runFacilitatorVerifier({
865
+ attempt,
866
+ option,
867
+ definition,
868
+ settle,
869
+ fetchImpl
870
+ }) {
871
+ if (option.proof.mode !== "x402" || attempt.type !== "x402" || !option.proof.facilitator) {
872
+ return {
873
+ success: false,
874
+ optionId: option.id,
875
+ attemptType: attempt.type,
876
+ failure: paymentFailureSchema.parse({
877
+ reason: "Facilitator verifier invoked for non-x402 option",
878
+ code: "verification_failed",
879
+ retryable: false
880
+ })
881
+ };
882
+ }
883
+ const facilitator = option.proof.facilitator;
884
+ const verifierUrl = new URL(
885
+ facilitator.verifyPath ?? "/verify",
886
+ ensureTrailingSlash(facilitator.url)
887
+ ).toString();
888
+ const requirement = toX402Requirement(option, definition.resource, option.settlement);
889
+ if (!requirement) {
890
+ return {
891
+ success: false,
892
+ optionId: option.id,
893
+ attemptType: attempt.type,
894
+ failure: paymentFailureSchema.parse({
895
+ reason: "Unable to derive x402 requirement for facilitator",
896
+ code: "verification_failed",
897
+ retryable: false
898
+ })
899
+ };
900
+ }
901
+ const headers = buildFacilitatorHeaders(facilitator);
902
+ const controller = facilitator.timeoutMs ? new AbortController() : void 0;
903
+ const timeout = facilitator.timeoutMs ? setTimeout(() => controller?.abort(), facilitator.timeoutMs) : void 0;
904
+ try {
905
+ const verifyResponse = await fetchImpl(verifierUrl, {
906
+ method: "POST",
907
+ headers,
908
+ body: JSON.stringify({
909
+ x402Version: attempt.payload.x402Version,
910
+ paymentHeader: attempt.raw,
911
+ paymentRequirements: requirement
912
+ }),
913
+ signal: controller?.signal ?? null
914
+ });
915
+ if (!verifyResponse.ok) {
916
+ return {
917
+ success: false,
918
+ optionId: option.id,
919
+ attemptType: attempt.type,
920
+ failure: paymentFailureSchema.parse({
921
+ reason: `Facilitator verify request failed: ${verifyResponse.status}`,
922
+ code: "verification_failed",
923
+ retryable: verifyResponse.status >= 500
924
+ })
925
+ };
926
+ }
927
+ const verifyPayload = await verifyResponse.json();
928
+ if (!verifyPayload.isValid) {
929
+ return {
930
+ success: false,
931
+ optionId: option.id,
932
+ attemptType: attempt.type,
933
+ failure: paymentFailureSchema.parse({
934
+ reason: verifyPayload.invalidReason ?? "Facilitator verification failed",
935
+ code: "verification_failed",
936
+ retryable: false
937
+ })
938
+ };
939
+ }
940
+ if (!settle) {
941
+ return {
942
+ success: true,
943
+ optionId: option.id,
944
+ attemptType: attempt.type,
945
+ metadata: paymentSuccessMetadataSchema.parse({
946
+ optionId: option.id,
947
+ verifier: facilitator.vendor ?? "facilitator"
948
+ })
949
+ };
950
+ }
951
+ const settleUrl = new URL(
952
+ facilitator.settlePath ?? "/settle",
953
+ ensureTrailingSlash(facilitator.url)
954
+ ).toString();
955
+ const settleResponse = await fetchImpl(settleUrl, {
956
+ method: "POST",
957
+ headers,
958
+ body: JSON.stringify({
959
+ x402Version: attempt.payload.x402Version,
960
+ paymentHeader: attempt.raw,
961
+ paymentRequirements: requirement
962
+ }),
963
+ signal: controller?.signal ?? null
964
+ });
965
+ if (!settleResponse.ok) {
966
+ return {
967
+ success: false,
968
+ optionId: option.id,
969
+ attemptType: attempt.type,
970
+ failure: paymentFailureSchema.parse({
971
+ reason: `Facilitator settle request failed: ${settleResponse.status}`,
972
+ code: "verification_failed",
973
+ retryable: settleResponse.status >= 500
974
+ })
975
+ };
976
+ }
977
+ const settlePayload = await settleResponse.json();
978
+ if (!settlePayload.success) {
979
+ return {
980
+ success: false,
981
+ optionId: option.id,
982
+ attemptType: attempt.type,
983
+ failure: paymentFailureSchema.parse({
984
+ reason: settlePayload.error ?? "Facilitator settlement failed",
985
+ code: "verification_failed",
986
+ retryable: false
987
+ })
988
+ };
989
+ }
990
+ const metadata = paymentSuccessMetadataSchema.parse({
991
+ optionId: option.id,
992
+ verifier: facilitator.vendor ?? "facilitator",
993
+ txHash: settlePayload.txHash ?? void 0,
994
+ networkId: settlePayload.networkId ?? void 0
995
+ });
996
+ return {
997
+ success: true,
998
+ optionId: option.id,
999
+ attemptType: attempt.type,
1000
+ metadata,
1001
+ responseHeaders: {
1002
+ [HEADER_PAYMENT_RESPONSE]: createPaymentResponseHeader(metadata)
1003
+ }
1004
+ };
1005
+ } catch (error) {
1006
+ return {
1007
+ success: false,
1008
+ optionId: option.id,
1009
+ attemptType: attempt.type,
1010
+ failure: paymentFailureSchema.parse({
1011
+ reason: `Facilitator request error: ${error.message}`,
1012
+ code: "verification_failed",
1013
+ retryable: false
1014
+ })
1015
+ };
1016
+ } finally {
1017
+ if (timeout) {
1018
+ clearTimeout(timeout);
1019
+ }
1020
+ }
1021
+ }
1022
+ function buildFacilitatorHeaders(config) {
1023
+ const headers = {
1024
+ "content-type": "application/json"
1025
+ };
1026
+ if (config?.headers) {
1027
+ Object.assign(headers, config.headers);
1028
+ }
1029
+ const apiKey = resolveFacilitatorApiKey(config);
1030
+ if (apiKey) {
1031
+ const headerName = config?.apiKeyHeader ?? "Authorization";
1032
+ headers[headerName] = apiKey;
1033
+ }
1034
+ return headers;
1035
+ }
1036
+ function resolveFacilitatorApiKey(config) {
1037
+ if (!config) {
1038
+ return void 0;
1039
+ }
1040
+ if (config.apiKey) {
1041
+ return config.apiKey;
1042
+ }
1043
+ if (config.apiKeyEnv && typeof process !== "undefined") {
1044
+ return process.env?.[config.apiKeyEnv];
1045
+ }
1046
+ return void 0;
1047
+ }
1048
+ function ensureTrailingSlash(value) {
1049
+ return value.endsWith("/") ? value : `${value}/`;
1050
+ }
1051
+
1052
+ // src/payment/index.ts
1053
+ var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.payment.context");
1054
+ var PaymentRequiredError = class extends Error {
1055
+ constructor(response, verification) {
1056
+ super("Payment required");
1057
+ this.name = "PaymentRequiredError";
1058
+ this.response = response;
1059
+ this.verification = verification;
1060
+ }
16
1061
  };
17
- Object.defineProperty(exports, "__esModule", { value: true });
18
- const commander_1 = require("commander");
19
- const build_1 = require("./build");
20
- const dev_1 = require("./dev");
21
- const validate_1 = require("./validate");
22
- const generate_metadata_1 = require("./generate-metadata");
23
- commander_1.program
24
- .name('opentool')
25
- .description('OpenTool CLI for building and developing serverless MCP tools')
26
- .version('1.0.0');
27
- // Dev command (like next dev)
28
- commander_1.program
29
- .command('dev')
30
- .description('Start MCP server with stdio transport')
31
- .option('-i, --input <dir>', 'Input directory containing tools', 'tools')
32
- .option('--watch', 'Watch for file changes', false)
33
- .action(dev_1.devCommand);
34
- // Build command (like next build)
35
- commander_1.program
36
- .command('build')
37
- .description('Build tools for deployment')
38
- .option('-i, --input <dir>', 'Input directory containing tools', 'tools')
39
- .option('-o, --output <dir>', 'Output directory for built tools', 'dist')
40
- .option('--name <name>', 'Server name', 'opentool-server')
41
- .option('--version <version>', 'Server version', '1.0.0')
42
- .action(build_1.buildCommand);
43
- // Validate command (metadata only)
44
- commander_1.program
45
- .command('validate')
46
- .description('Validate metadata for registry submission')
47
- .option('-i, --input <dir>', 'Input directory containing tools', 'tools')
48
- .action(validate_1.validateCommand);
49
- // Full validation command (tools + metadata)
50
- commander_1.program
51
- .command('validate-full')
52
- .description('Full validation of tools and metadata')
53
- .option('-i, --input <dir>', 'Input directory containing tools', 'tools')
54
- .action(validate_1.validateFullCommand);
55
- // Generate metadata command
56
- commander_1.program
57
- .command('metadata')
58
- .description('Generate OpenTool metadata JSON without building')
59
- .option('-i, --input <dir>', 'Input directory containing tools', 'tools')
60
- .option('-o, --output <file>', 'Output file path for metadata.json', 'metadata.json')
61
- .option('--name <name>', 'Server name', 'opentool-server')
62
- .option('--version <version>', 'Server version', '1.0.0')
63
- .action(generate_metadata_1.generateMetadataCommand);
64
- // Parse arguments
65
- commander_1.program.parse();
66
- __exportStar(require("./build"), exports);
67
- __exportStar(require("./dev"), exports);
68
- __exportStar(require("./validate"), exports);
69
- __exportStar(require("./generate-metadata"), exports);
1062
+ function setPaymentContext(request, context) {
1063
+ try {
1064
+ Object.defineProperty(request, PAYMENT_CONTEXT_SYMBOL, {
1065
+ value: context,
1066
+ configurable: true,
1067
+ enumerable: false,
1068
+ writable: true
1069
+ });
1070
+ } catch {
1071
+ request[PAYMENT_CONTEXT_SYMBOL] = context;
1072
+ }
1073
+ }
1074
+ function applyPaymentHeaders(response, headers) {
1075
+ const entries = Object.entries(headers ?? {});
1076
+ if (entries.length === 0) {
1077
+ return response;
1078
+ }
1079
+ let mutated = false;
1080
+ const merged = new Headers(response.headers);
1081
+ for (const [key, value] of entries) {
1082
+ if (!merged.has(key)) {
1083
+ merged.set(key, value);
1084
+ mutated = true;
1085
+ }
1086
+ }
1087
+ if (!mutated) {
1088
+ return response;
1089
+ }
1090
+ return new Response(response.body, {
1091
+ status: response.status,
1092
+ statusText: response.statusText,
1093
+ headers: merged
1094
+ });
1095
+ }
1096
+ function withPaymentRequirement(handler, payment, options = {}) {
1097
+ return async (request) => {
1098
+ const verification = await requirePayment(request, payment, options);
1099
+ if (verification instanceof Response) {
1100
+ return verification;
1101
+ }
1102
+ setPaymentContext(request, verification);
1103
+ const response = await Promise.resolve(handler(request));
1104
+ return applyPaymentHeaders(response, verification.headers);
1105
+ };
1106
+ }
1107
+ async function requirePayment(request, payment, options = {}) {
1108
+ const { definition, verifiers } = normalizePayment(payment);
1109
+ const mergedVerifiers = {
1110
+ ...verifiers,
1111
+ ...options.verifiers ?? {}
1112
+ };
1113
+ const verifyOptions = {
1114
+ definition,
1115
+ request
1116
+ };
1117
+ if (Object.keys(mergedVerifiers).length > 0) {
1118
+ verifyOptions.verifiers = mergedVerifiers;
1119
+ }
1120
+ if (options.settle !== void 0) {
1121
+ verifyOptions.settle = options.settle;
1122
+ }
1123
+ if (options.fetchImpl) {
1124
+ verifyOptions.fetchImpl = options.fetchImpl;
1125
+ }
1126
+ const verification = await verifyPayment(verifyOptions);
1127
+ if (!verification.success || !verification.metadata) {
1128
+ if (options.onFailure) {
1129
+ return options.onFailure(verification);
1130
+ }
1131
+ const response = paymentRequiredResponse(definition);
1132
+ throw new PaymentRequiredError(response, verification);
1133
+ }
1134
+ return {
1135
+ payment: verification.metadata,
1136
+ headers: verification.responseHeaders ?? {},
1137
+ optionId: verification.optionId,
1138
+ result: verification
1139
+ };
1140
+ }
1141
+ function normalizePayment(payment) {
1142
+ if (isDefinedPayment(payment)) {
1143
+ return {
1144
+ definition: payment.definition,
1145
+ verifiers: payment.verifiers ?? {}
1146
+ };
1147
+ }
1148
+ return {
1149
+ definition: payment,
1150
+ verifiers: {}
1151
+ };
1152
+ }
1153
+ function isDefinedPayment(value) {
1154
+ return !!value && typeof value === "object" && "definition" in value && value.definition !== void 0;
1155
+ }
1156
+
1157
+ // src/adapters/mcp.ts
1158
+ function createMcpAdapter(options) {
1159
+ const normalizedSchema = ensureSchema(options.schema);
1160
+ const defaultMethod = resolveDefaultMethod(options);
1161
+ const httpHandler = options.httpHandlers[defaultMethod];
1162
+ if (!httpHandler) {
1163
+ throw new Error(
1164
+ `Tool "${options.name}" does not export an HTTP handler for ${defaultMethod}`
1165
+ );
1166
+ }
1167
+ return async function invoke(rawArguments) {
1168
+ const validated = normalizedSchema ? normalizedSchema.parse(rawArguments ?? {}) : rawArguments;
1169
+ const request = buildRequest(options.name, defaultMethod, validated);
1170
+ try {
1171
+ const response = await Promise.resolve(httpHandler(request));
1172
+ return await responseToToolResponse(response);
1173
+ } catch (error) {
1174
+ if (error instanceof PaymentRequiredError) {
1175
+ return await responseToToolResponse(error.response);
1176
+ }
1177
+ throw error;
1178
+ }
1179
+ };
1180
+ }
1181
+ function resolveDefaultMethod(options) {
1182
+ const explicit = options.defaultMethod?.toUpperCase();
1183
+ if (explicit && typeof options.httpHandlers[explicit] === "function") {
1184
+ return explicit;
1185
+ }
1186
+ const preferredOrder = ["POST", "PUT", "PATCH", "GET", "DELETE", "OPTIONS", "HEAD"];
1187
+ for (const method of preferredOrder) {
1188
+ if (typeof options.httpHandlers[method] === "function") {
1189
+ return method;
1190
+ }
1191
+ }
1192
+ const available = Object.keys(options.httpHandlers).filter(
1193
+ (method) => typeof options.httpHandlers[method] === "function"
1194
+ );
1195
+ if (available.length > 0) {
1196
+ return available[0];
1197
+ }
1198
+ throw new Error(`No HTTP handlers available for tool "${options.name}"`);
1199
+ }
1200
+ function ensureSchema(schema) {
1201
+ if (!schema) {
1202
+ return void 0;
1203
+ }
1204
+ if (schema instanceof z.ZodType) {
1205
+ return schema;
1206
+ }
1207
+ if (typeof schema?.parse === "function") {
1208
+ return schema;
1209
+ }
1210
+ throw new Error("MCP adapter requires a valid Zod schema to validate arguments");
1211
+ }
1212
+ function buildRequest(name, method, params) {
1213
+ const url = new URL(`https://opentool.local/${encodeURIComponent(name)}`);
1214
+ const headers = new Headers({
1215
+ "x-opentool-invocation": "mcp",
1216
+ "x-opentool-tool": name
1217
+ });
1218
+ if (method === "GET" || method === "HEAD") {
1219
+ if (params && typeof params === "object") {
1220
+ Object.entries(params).forEach(([key, value]) => {
1221
+ if (value == null) {
1222
+ return;
1223
+ }
1224
+ url.searchParams.set(key, String(value));
1225
+ });
1226
+ }
1227
+ return new Request(url, { method, headers });
1228
+ }
1229
+ headers.set("Content-Type", "application/json");
1230
+ const init = { method, headers };
1231
+ if (params != null) {
1232
+ init.body = JSON.stringify(params);
1233
+ }
1234
+ return new Request(url, init);
1235
+ }
1236
+ async function responseToToolResponse(response) {
1237
+ const statusIsError = response.status >= 400;
1238
+ const contentType = response.headers.get("content-type") ?? "";
1239
+ const text = await response.text();
1240
+ if (contentType.includes("application/json")) {
1241
+ try {
1242
+ const payload = text ? JSON.parse(text) : {};
1243
+ if (payload && typeof payload === "object" && Array.isArray(payload.content)) {
1244
+ return {
1245
+ content: payload.content,
1246
+ isError: payload.isError ?? statusIsError
1247
+ };
1248
+ }
1249
+ return {
1250
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
1251
+ isError: statusIsError
1252
+ };
1253
+ } catch {
1254
+ return {
1255
+ content: [{ type: "text", text }],
1256
+ isError: statusIsError
1257
+ };
1258
+ }
1259
+ }
1260
+ if (!text) {
1261
+ return {
1262
+ content: [],
1263
+ isError: statusIsError
1264
+ };
1265
+ }
1266
+ return {
1267
+ content: [{ type: "text", text }],
1268
+ isError: statusIsError
1269
+ };
1270
+ }
1271
+
1272
+ // src/types/index.ts
1273
+ var HTTP_METHODS = [
1274
+ "GET",
1275
+ "HEAD",
1276
+ "POST",
1277
+ "PUT",
1278
+ "DELETE",
1279
+ "PATCH",
1280
+ "OPTIONS"
1281
+ ];
1282
+
1283
+ // src/cli/validate.ts
1284
+ var SUPPORTED_EXTENSIONS = [
1285
+ ".ts",
1286
+ ".tsx",
1287
+ ".js",
1288
+ ".jsx",
1289
+ ".mjs",
1290
+ ".cjs"
1291
+ ];
1292
+ async function validateCommand(options) {
1293
+ console.log("\u{1F50D} Validating OpenTool metadata...");
1294
+ try {
1295
+ const toolsDir = path5.resolve(options.input);
1296
+ if (!fs4.existsSync(toolsDir)) {
1297
+ throw new Error(`Tools directory not found: ${toolsDir}`);
1298
+ }
1299
+ const projectRoot = path5.dirname(toolsDir);
1300
+ const tools = await loadAndValidateTools(toolsDir, { projectRoot });
1301
+ if (tools.length === 0) {
1302
+ throw new Error("No valid tools found - metadata validation aborted");
1303
+ }
1304
+ const { metadata, defaultsApplied, sourceMetadataPath } = await buildMetadataArtifact({
1305
+ projectRoot,
1306
+ tools
1307
+ });
1308
+ logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath);
1309
+ console.log("\n\u2705 Metadata validation passed!\n");
1310
+ } catch (error) {
1311
+ console.error("\u274C Metadata validation failed:", error);
1312
+ process.exit(1);
1313
+ }
1314
+ }
1315
+ async function validateFullCommand(options) {
1316
+ console.log("\u{1F50D} Running full OpenTool validation...\n");
1317
+ try {
1318
+ const toolsDir = path5.resolve(options.input);
1319
+ if (!fs4.existsSync(toolsDir)) {
1320
+ throw new Error(`Tools directory not found: ${toolsDir}`);
1321
+ }
1322
+ const projectRoot = path5.dirname(toolsDir);
1323
+ const tools = await loadAndValidateTools(toolsDir, { projectRoot });
1324
+ if (tools.length === 0) {
1325
+ throw new Error("No tools discovered in the target directory");
1326
+ }
1327
+ const names = tools.map((tool) => tool.metadata?.name ?? tool.filename);
1328
+ const duplicates = findDuplicates(names);
1329
+ if (duplicates.length > 0) {
1330
+ throw new Error(`Duplicate tool names found: ${duplicates.join(", ")}`);
1331
+ }
1332
+ const { metadata, defaultsApplied, sourceMetadataPath } = await buildMetadataArtifact({
1333
+ projectRoot,
1334
+ tools
1335
+ });
1336
+ console.log(`\u{1F4E6} Tools loaded: ${tools.length}`);
1337
+ tools.forEach((tool) => {
1338
+ const toolName = tool.metadata?.name ?? tool.filename;
1339
+ const description = tool.metadata?.description ?? `${toolName} tool`;
1340
+ console.log(` \u2022 ${toolName} \u2014 ${description}`);
1341
+ });
1342
+ logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath);
1343
+ console.log("\n\u2705 Full validation completed successfully\n");
1344
+ } catch (error) {
1345
+ console.error("\u274C Full validation failed:", error);
1346
+ process.exit(1);
1347
+ }
1348
+ }
1349
+ async function loadAndValidateTools(toolsDir, options = {}) {
1350
+ const files = fs4.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path5.extname(file)));
1351
+ if (files.length === 0) {
1352
+ return [];
1353
+ }
1354
+ const projectRoot = options.projectRoot ?? path5.dirname(toolsDir);
1355
+ const tempDir = path5.join(toolsDir, ".opentool-temp");
1356
+ if (fs4.existsSync(tempDir)) {
1357
+ fs4.rmSync(tempDir, { recursive: true, force: true });
1358
+ }
1359
+ const entryPoints = files.map((file) => path5.join(toolsDir, file));
1360
+ const { outDir, cleanup } = await transpileWithEsbuild({
1361
+ entryPoints,
1362
+ projectRoot,
1363
+ format: "esm",
1364
+ outDir: tempDir
1365
+ });
1366
+ const tools = [];
1367
+ try {
1368
+ for (const file of files) {
1369
+ const compiledPath = resolveCompiledPath(outDir, file);
1370
+ if (!fs4.existsSync(compiledPath)) {
1371
+ throw new Error(`Failed to compile ${file}`);
1372
+ }
1373
+ const moduleExports = await importFresh(compiledPath);
1374
+ const toolModule = extractToolModule(moduleExports, file);
1375
+ const schema = ensureZodSchema(toolModule.schema, file);
1376
+ const paymentExport = toolModule.payment;
1377
+ const toolName = toolModule.metadata?.name ?? toolModule.metadata?.title ?? toBaseName(file);
1378
+ const inputSchemaRaw = schema ? toJsonSchema(toolName, schema) : void 0;
1379
+ const inputSchema = normalizeInputSchema(inputSchemaRaw);
1380
+ const httpHandlersRaw = collectHttpHandlers(toolModule, file);
1381
+ const httpHandlers = [...httpHandlersRaw];
1382
+ if (httpHandlers.length === 0) {
1383
+ throw new Error(
1384
+ `${file} must export at least one HTTP handler (e.g. POST)`
1385
+ );
1386
+ }
1387
+ if (paymentExport) {
1388
+ for (let index = 0; index < httpHandlers.length; index += 1) {
1389
+ const entry = httpHandlers[index];
1390
+ httpHandlers[index] = {
1391
+ ...entry,
1392
+ handler: withPaymentRequirement(entry.handler, paymentExport)
1393
+ };
1394
+ }
1395
+ }
1396
+ const httpHandlerMap = toHttpHandlerMap(httpHandlers);
1397
+ const defaultMethod = typeof toolModule.mcp?.defaultMethod === "string" ? toolModule.mcp.defaultMethod : void 0;
1398
+ const adapter = createMcpAdapter({
1399
+ name: toolName,
1400
+ httpHandlers: httpHandlerMap,
1401
+ ...defaultMethod ? { defaultMethod } : {},
1402
+ ...schema ? { schema } : {}
1403
+ });
1404
+ let metadataOverrides = toolModule.metadata ?? null;
1405
+ if (paymentExport?.metadata) {
1406
+ if (metadataOverrides) {
1407
+ metadataOverrides = {
1408
+ ...metadataOverrides,
1409
+ payment: metadataOverrides.payment ?? paymentExport.metadata,
1410
+ annotations: {
1411
+ ...metadataOverrides.annotations ?? {},
1412
+ requiresPayment: metadataOverrides.annotations?.requiresPayment ?? true
1413
+ }
1414
+ };
1415
+ } else {
1416
+ metadataOverrides = {
1417
+ payment: paymentExport.metadata,
1418
+ annotations: { requiresPayment: true }
1419
+ };
1420
+ }
1421
+ }
1422
+ const tool = {
1423
+ schema: schema ?? void 0,
1424
+ inputSchema,
1425
+ metadata: metadataOverrides,
1426
+ httpHandlers,
1427
+ mcpConfig: normalizeMcpConfig(toolModule.mcp, file),
1428
+ filename: toBaseName(file),
1429
+ sourcePath: path5.join(toolsDir, file),
1430
+ handler: async (params) => adapter(params),
1431
+ payment: paymentExport ?? null
1432
+ };
1433
+ tools.push(tool);
1434
+ }
1435
+ } finally {
1436
+ cleanup();
1437
+ if (fs4.existsSync(tempDir)) {
1438
+ fs4.rmSync(tempDir, { recursive: true, force: true });
1439
+ }
1440
+ }
1441
+ return tools;
1442
+ }
1443
+ function extractToolModule(exportsObject, filename) {
1444
+ const candidates = [exportsObject, exportsObject?.default];
1445
+ for (const candidate of candidates) {
1446
+ if (candidate && typeof candidate === "object") {
1447
+ const hasSchema = candidate.schema && typeof candidate.schema === "object";
1448
+ const hasHttp = HTTP_METHODS.some((method) => typeof candidate[method] === "function");
1449
+ if (hasSchema || hasHttp) {
1450
+ return candidate;
1451
+ }
1452
+ }
1453
+ }
1454
+ throw new Error(
1455
+ `${filename} must export a tool definition. Expected a Zod schema plus HTTP handlers (export async function POST).`
1456
+ );
1457
+ }
1458
+ function toJsonSchema(name, schema) {
1459
+ if (!schema) {
1460
+ return void 0;
1461
+ }
1462
+ try {
1463
+ return zodToJsonSchema(schema, {
1464
+ name: `${name}Schema`,
1465
+ target: "jsonSchema7",
1466
+ $refStrategy: "none"
1467
+ });
1468
+ } catch (error) {
1469
+ throw new Error(`Failed to convert Zod schema for ${name}: ${error}`);
1470
+ }
1471
+ }
1472
+ function toBaseName(file) {
1473
+ return file.replace(/\.[^.]+$/, "");
1474
+ }
1475
+ function ensureZodSchema(schemaCandidate, filename) {
1476
+ if (schemaCandidate == null) {
1477
+ return void 0;
1478
+ }
1479
+ if (schemaCandidate instanceof z.ZodType) {
1480
+ return schemaCandidate;
1481
+ }
1482
+ const schema = schemaCandidate;
1483
+ if (typeof schema?.parse !== "function") {
1484
+ throw new Error(`${filename} schema export must be a Zod schema (missing parse method)`);
1485
+ }
1486
+ return schema;
1487
+ }
1488
+ function collectHttpHandlers(module, filename) {
1489
+ const handlers = [];
1490
+ for (const method of HTTP_METHODS) {
1491
+ const handler = module?.[method];
1492
+ if (typeof handler === "function") {
1493
+ handlers.push({
1494
+ method,
1495
+ handler: async (request) => handler.call(module, request)
1496
+ });
1497
+ }
1498
+ }
1499
+ handlers.sort((a, b) => HTTP_METHODS.indexOf(a.method) - HTTP_METHODS.indexOf(b.method));
1500
+ const duplicates = findDuplicates(handlers.map((h) => h.method));
1501
+ if (duplicates.length > 0) {
1502
+ throw new Error(
1503
+ `${filename} exports multiple handlers for HTTP method(s): ${duplicates.join(", ")}`
1504
+ );
1505
+ }
1506
+ return handlers;
1507
+ }
1508
+ function toHttpHandlerMap(handlers) {
1509
+ return handlers.reduce((acc, handler) => {
1510
+ acc[handler.method.toUpperCase()] = handler.handler;
1511
+ return acc;
1512
+ }, {});
1513
+ }
1514
+ function normalizeInputSchema(schema) {
1515
+ if (!schema || typeof schema !== "object") {
1516
+ return schema;
1517
+ }
1518
+ const clone = JSON.parse(JSON.stringify(schema));
1519
+ if (typeof clone.$ref === "string" && clone.$ref.startsWith("#/definitions/")) {
1520
+ const refName = clone.$ref.replace("#/definitions/", "");
1521
+ const definitions = clone.definitions;
1522
+ if (definitions && typeof definitions[refName] === "object") {
1523
+ return normalizeInputSchema(definitions[refName]);
1524
+ }
1525
+ }
1526
+ delete clone.$ref;
1527
+ delete clone.definitions;
1528
+ if (!("type" in clone)) {
1529
+ clone.type = "object";
1530
+ }
1531
+ return clone;
1532
+ }
1533
+ function normalizeMcpConfig(rawConfig, filename) {
1534
+ if (rawConfig == null) {
1535
+ return null;
1536
+ }
1537
+ if (rawConfig === false) {
1538
+ return null;
1539
+ }
1540
+ if (rawConfig === true) {
1541
+ return { enabled: true };
1542
+ }
1543
+ if (!isPlainObject(rawConfig)) {
1544
+ throw new Error(`${filename} export \\"mcp\\" must be an object with an enabled flag`);
1545
+ }
1546
+ const enabledRaw = rawConfig.enabled;
1547
+ if (enabledRaw === false) {
1548
+ return null;
1549
+ }
1550
+ if (enabledRaw !== true) {
1551
+ throw new Error(`${filename} mcp.enabled must be explicitly set to true to opt-in to MCP`);
1552
+ }
1553
+ const modeRaw = rawConfig.mode;
1554
+ let mode;
1555
+ if (typeof modeRaw === "string") {
1556
+ const normalized = modeRaw.toLowerCase();
1557
+ if (["stdio", "lambda", "dual"].includes(normalized)) {
1558
+ mode = normalized;
1559
+ } else {
1560
+ throw new Error(
1561
+ `${filename} mcp.mode must be one of "stdio", "lambda", or "dual" if specified`
1562
+ );
1563
+ }
1564
+ }
1565
+ const defaultMethodRaw = rawConfig.defaultMethod;
1566
+ const defaultMethod = typeof defaultMethodRaw === "string" ? defaultMethodRaw.toUpperCase() : void 0;
1567
+ const overridesRaw = rawConfig.metadataOverrides;
1568
+ const metadataOverrides = isPlainObject(overridesRaw) ? overridesRaw : void 0;
1569
+ const config = {
1570
+ enabled: true
1571
+ };
1572
+ if (mode) {
1573
+ config.mode = mode;
1574
+ }
1575
+ if (defaultMethod) {
1576
+ config.defaultMethod = defaultMethod;
1577
+ }
1578
+ if (metadataOverrides) {
1579
+ config.metadataOverrides = metadataOverrides;
1580
+ }
1581
+ return config;
1582
+ }
1583
+ function isPlainObject(value) {
1584
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1585
+ }
1586
+ function findDuplicates(values) {
1587
+ const seen = /* @__PURE__ */ new Map();
1588
+ const duplicates = /* @__PURE__ */ new Set();
1589
+ values.forEach((value) => {
1590
+ const count = seen.get(value) ?? 0;
1591
+ seen.set(value, count + 1);
1592
+ if (count >= 1) {
1593
+ duplicates.add(value);
1594
+ }
1595
+ });
1596
+ return Array.from(duplicates.values());
1597
+ }
1598
+ function logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath) {
1599
+ console.log(`\u{1F4C4} metadata loaded from ${sourceMetadataPath}`);
1600
+ console.log("\n\u{1F4CA} Metadata Summary:");
1601
+ console.log(` \u2022 Name: ${metadata.name}`);
1602
+ console.log(` \u2022 Display Name: ${metadata.displayName}`);
1603
+ console.log(` \u2022 Version: ${metadata.version}`);
1604
+ console.log(` \u2022 Category: ${metadata.category}`);
1605
+ console.log(` \u2022 Tools: ${metadata.tools.length}`);
1606
+ console.log(` \u2022 Spec Version: ${metadata.metadataSpecVersion}`);
1607
+ if (metadata.payment) {
1608
+ console.log(` \u2022 Payment: $${metadata.payment.amountUSDC} USDC`);
1609
+ }
1610
+ if (defaultsApplied.length > 0) {
1611
+ console.log("\nDefaults applied during metadata synthesis:");
1612
+ defaultsApplied.forEach((entry) => console.log(` \u2022 ${entry}`));
1613
+ }
1614
+ }
1615
+
1616
+ // src/cli/build.ts
1617
+ async function buildCommand(options) {
1618
+ const start = timestamp();
1619
+ console.log(`[${start}] Building OpenTool project...`);
1620
+ try {
1621
+ const artifacts = await buildProject(options);
1622
+ logBuildSummary(artifacts, options);
1623
+ } catch (error) {
1624
+ const end = timestamp();
1625
+ console.error(`[${end}] Build failed:`, error);
1626
+ process.exit(1);
1627
+ }
1628
+ }
1629
+ async function buildProject(options) {
1630
+ const toolsDir = path5.resolve(options.input);
1631
+ if (!fs4.existsSync(toolsDir)) {
1632
+ throw new Error(`Tools directory not found: ${toolsDir}`);
1633
+ }
1634
+ const projectRoot = path5.dirname(toolsDir);
1635
+ const outputDir = path5.resolve(options.output);
1636
+ fs4.mkdirSync(outputDir, { recursive: true });
1637
+ const serverName = options.name ?? "opentool-server";
1638
+ const serverVersion = options.version ?? "1.0.0";
1639
+ const tools = await loadAndValidateTools(toolsDir, { projectRoot });
1640
+ if (tools.length === 0) {
1641
+ throw new Error("No valid tools found - build aborted");
1642
+ }
1643
+ const { metadata, defaultsApplied } = await buildMetadataArtifact({
1644
+ projectRoot,
1645
+ tools
1646
+ });
1647
+ const metadataPath = path5.join(outputDir, "metadata.json");
1648
+ fs4.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
1649
+ const compiledTools = await emitTools(tools, {
1650
+ projectRoot,
1651
+ outputDir
1652
+ });
1653
+ const shouldBuildMcpServer = compiledTools.some((artifact) => artifact.mcpEnabled);
1654
+ if (shouldBuildMcpServer) {
1655
+ await writeMcpServer({
1656
+ outputDir,
1657
+ serverName,
1658
+ serverVersion,
1659
+ compiledTools});
1660
+ } else {
1661
+ const serverPath = path5.join(outputDir, "mcp-server.js");
1662
+ if (fs4.existsSync(serverPath)) {
1663
+ fs4.rmSync(serverPath);
1664
+ }
1665
+ }
1666
+ return {
1667
+ metadata,
1668
+ defaultsApplied,
1669
+ tools,
1670
+ compiledTools
1671
+ };
1672
+ }
1673
+ async function emitTools(tools, config) {
1674
+ const toolsOutDir = path5.join(config.outputDir, "tools");
1675
+ if (fs4.existsSync(toolsOutDir)) {
1676
+ fs4.rmSync(toolsOutDir, { recursive: true, force: true });
1677
+ }
1678
+ fs4.mkdirSync(toolsOutDir, { recursive: true });
1679
+ const entryPoints = tools.map((tool) => {
1680
+ if (!tool.sourcePath) {
1681
+ throw new Error(`Missing sourcePath for tool ${tool.filename}`);
1682
+ }
1683
+ return tool.sourcePath;
1684
+ });
1685
+ await transpileWithEsbuild({
1686
+ entryPoints,
1687
+ projectRoot: config.projectRoot,
1688
+ format: "cjs",
1689
+ outDir: toolsOutDir
1690
+ });
1691
+ const compiled = tools.map((tool) => {
1692
+ if (!tool.sourcePath) {
1693
+ throw new Error(`Missing sourcePath for tool ${tool.filename}`);
1694
+ }
1695
+ const base = path5.basename(tool.sourcePath).replace(/\.[^.]+$/, "");
1696
+ const modulePath = path5.join("tools", `${base}.js`);
1697
+ if (!fs4.existsSync(path5.join(config.outputDir, modulePath))) {
1698
+ throw new Error(`Expected compiled output missing: ${modulePath}`);
1699
+ }
1700
+ const defaultMcpMethod = tool.mcpConfig?.defaultMethod;
1701
+ return {
1702
+ name: tool.metadata?.name ?? tool.filename,
1703
+ filename: base,
1704
+ modulePath,
1705
+ httpMethods: tool.httpHandlers.map((handler) => handler.method),
1706
+ mcpEnabled: tool.mcpConfig?.enabled ?? false,
1707
+ ...defaultMcpMethod ? { defaultMcpMethod } : {},
1708
+ hasWallet: Boolean(tool.payment)
1709
+ };
1710
+ });
1711
+ return compiled;
1712
+ }
1713
+ function renderMcpServer(options) {
1714
+ const toolImports = options.compiledTools.map((tool, index) => `const tool${index} = require('./${tool.modulePath}');`).join("\n");
1715
+ const registry = options.compiledTools.map((artifact, index) => {
1716
+ const config = {
1717
+ enabled: artifact.mcpEnabled,
1718
+ defaultMethod: artifact.defaultMcpMethod ?? null,
1719
+ httpMethods: artifact.httpMethods,
1720
+ filename: artifact.filename
1721
+ };
1722
+ return ` { meta: metadata.tools[${index}], module: tool${index}, config: ${JSON.stringify(config)} }`;
1723
+ }).join(",\n");
1724
+ return `#!/usr/bin/env node
1725
+ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
1726
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
1727
+ const { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
1728
+ const metadata = require('./metadata.json');
1729
+ const { createMcpAdapter, HTTP_METHODS } = require('opentool/dist/adapters/mcp.js');
1730
+
1731
+ ${toolImports}
1732
+
1733
+ const toolRegistry = [
1734
+ ${registry}
1735
+ ];
1736
+
1737
+ const adapters = toolRegistry.map((entry) => {
1738
+ if (!entry.config.enabled) {
1739
+ return null;
1740
+ }
1741
+
1742
+ const httpHandlers = Object.fromEntries(
1743
+ HTTP_METHODS
1744
+ .map((method) => [method, entry.module[method]])
1745
+ .filter(([, handler]) => typeof handler === 'function')
1746
+ );
1747
+
1748
+ return {
1749
+ meta: entry.meta,
1750
+ invoke: createMcpAdapter({
1751
+ name: entry.meta.name,
1752
+ schema: entry.module.schema,
1753
+ httpHandlers,
1754
+ defaultMethod: entry.config.defaultMethod || undefined,
1755
+ }),
1756
+ };
1757
+ });
1758
+
1759
+ const server = new Server(
1760
+ {
1761
+ name: '${escapeForJs(options.serverName)}',
1762
+ version: '${escapeForJs(options.serverVersion)}',
1763
+ },
1764
+ {
1765
+ capabilities: {
1766
+ tools: {},
1767
+ },
1768
+ }
1769
+ );
1770
+
1771
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1772
+ tools: adapters
1773
+ .filter((entry) => entry !== null)
1774
+ .map((entry) => entry.meta),
1775
+ }));
1776
+
1777
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1778
+ const adapter = adapters.find((entry) => entry && entry.meta.name === request.params.name);
1779
+ if (!adapter) {
1780
+ throw new Error('Tool ' + request.params.name + ' is not registered for MCP');
1781
+ }
1782
+
1783
+ try {
1784
+ return await adapter.invoke(request.params.arguments);
1785
+ } catch (error) {
1786
+ const message = (error && error.message) || String(error);
1787
+ return {
1788
+ content: [{ type: 'text', text: 'Error: ' + message }],
1789
+ isError: true,
1790
+ };
1791
+ }
1792
+ });
1793
+
1794
+ async function main() {
1795
+ const transport = new StdioServerTransport();
1796
+ await server.connect(transport);
1797
+ }
1798
+
1799
+ if (require.main === module) {
1800
+ main().catch(console.error);
1801
+ }
1802
+
1803
+ module.exports = { server };
1804
+ `;
1805
+ }
1806
+ async function writeMcpServer(options) {
1807
+ const serverCode = renderMcpServer(options);
1808
+ const serverPath = path5.join(options.outputDir, "mcp-server.js");
1809
+ fs4.writeFileSync(serverPath, serverCode);
1810
+ fs4.chmodSync(serverPath, 493);
1811
+ }
1812
+ function logBuildSummary(artifacts, options) {
1813
+ const end = timestamp();
1814
+ console.log(`[${end}] Build completed successfully!`);
1815
+ console.log(`Output directory: ${path5.resolve(options.output)}`);
1816
+ console.log("Generated files:");
1817
+ const hasMcp = artifacts.compiledTools.some((tool) => tool.mcpEnabled);
1818
+ if (hasMcp) {
1819
+ console.log(" \u2022 mcp-server.js (stdio server)");
1820
+ }
1821
+ console.log(` \u2022 tools/ (${artifacts.compiledTools.length} compiled tools)`);
1822
+ artifacts.compiledTools.forEach((tool) => {
1823
+ const methods = tool.httpMethods.join(", ");
1824
+ const walletBadge = tool.hasWallet ? " [wallet]" : "";
1825
+ console.log(` - ${tool.name} [${methods}]${walletBadge}`);
1826
+ });
1827
+ console.log(" \u2022 metadata.json (registry artifact)");
1828
+ if (artifacts.defaultsApplied.length > 0) {
1829
+ console.log("\nDefaults applied during metadata synthesis:");
1830
+ artifacts.defaultsApplied.forEach((entry) => console.log(` \u2022 ${entry}`));
1831
+ }
1832
+ if (!hasMcp) {
1833
+ console.log("\n\u2139\uFE0F MCP adapter skipped (no tools opted in)");
1834
+ }
1835
+ }
1836
+ function timestamp() {
1837
+ return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
1838
+ }
1839
+ function escapeForJs(value) {
1840
+ return value.replace(/'/g, "\\'");
1841
+ }
1842
+ var __dirname2 = path5.dirname(fileURLToPath(import.meta.url));
1843
+ var packageJson = JSON.parse(
1844
+ fs4.readFileSync(path5.resolve(__dirname2, "../../package.json"), "utf-8")
1845
+ );
1846
+ var cyan = "\x1B[36m";
1847
+ var bold = "\x1B[1m";
1848
+ var dim = "\x1B[2m";
1849
+ var reset = "\x1B[0m";
1850
+ async function devCommand(options) {
1851
+ const port = options.port ?? 7e3;
1852
+ const watch2 = options.watch ?? true;
1853
+ const enableStdio = options.stdio ?? false;
1854
+ const log = enableStdio ? (_message) => {
1855
+ } : (message) => console.log(message);
1856
+ try {
1857
+ const toolsDir = path5.resolve(options.input);
1858
+ if (!fs4.existsSync(toolsDir)) {
1859
+ throw new Error(`Tools directory not found: ${toolsDir}`);
1860
+ }
1861
+ const projectRoot = path5.dirname(toolsDir);
1862
+ let toolDefinitions = await loadToolDefinitions(toolsDir, projectRoot);
1863
+ if (toolDefinitions.length === 0) {
1864
+ throw new Error("No tools found in the target directory");
1865
+ }
1866
+ let routes = expandRoutes(toolDefinitions);
1867
+ const stdioController = enableStdio ? await startMcpServer(() => toolDefinitions) : null;
1868
+ if (watch2) {
1869
+ fs4.watch(toolsDir, async (_eventType, filename) => {
1870
+ if (filename && !/\.(ts|js|mjs|cjs|tsx|jsx)$/.test(filename)) {
1871
+ return;
1872
+ }
1873
+ log(
1874
+ `${dim}
1875
+ Detected change in ${filename ?? "tools directory"}, reloading...${reset}`
1876
+ );
1877
+ try {
1878
+ toolDefinitions = await loadToolDefinitions(toolsDir, projectRoot);
1879
+ routes = expandRoutes(toolDefinitions);
1880
+ logReload(toolDefinitions, enableStdio, log);
1881
+ } catch (error) {
1882
+ console.error("Failed to reload tools:", error);
1883
+ }
1884
+ });
1885
+ }
1886
+ const server = http.createServer(async (req, res) => {
1887
+ const method = (req.method || "GET").toUpperCase();
1888
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
1889
+ const routePath = url.pathname;
1890
+ log(`${dim}[request] ${method} ${routePath}${reset}`);
1891
+ try {
1892
+ await handleRequest({ req, res, port, routes });
1893
+ log(
1894
+ `${dim}[response] ${method} ${routePath} ${res.statusCode}${reset}`
1895
+ );
1896
+ } catch (error) {
1897
+ console.error("Error handling request:", error);
1898
+ res.writeHead(500, { "Content-Type": "application/json" });
1899
+ res.end(JSON.stringify({ error: error.message }));
1900
+ log(`${dim}[response] ${method} ${routePath} 500${reset}`);
1901
+ }
1902
+ });
1903
+ server.listen(port, () => {
1904
+ log(`${bold}${dim}> dev opentool${reset}`);
1905
+ log(
1906
+ ` * ${bold}opentool${reset} ${cyan}v${packageJson.version}${reset}`
1907
+ );
1908
+ log(` * ${bold}HTTP:${reset} http://localhost:${port}`);
1909
+ logStartup(toolDefinitions, enableStdio, log);
1910
+ });
1911
+ process.on("SIGINT", async () => {
1912
+ log(`
1913
+ ${dim}Shutting down dev server...${reset}`);
1914
+ server.close();
1915
+ if (stdioController) {
1916
+ await stdioController.close();
1917
+ }
1918
+ process.exit(0);
1919
+ });
1920
+ } catch (error) {
1921
+ console.error("Dev server failed:", error);
1922
+ process.exit(1);
1923
+ }
1924
+ }
1925
+ async function startMcpServer(getTools) {
1926
+ const server = new Server(
1927
+ {
1928
+ name: "opentool-dev",
1929
+ version: "1.0.0"
1930
+ },
1931
+ {
1932
+ capabilities: {
1933
+ tools: {}
1934
+ }
1935
+ }
1936
+ );
1937
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1938
+ const tools = getTools().filter(isMcpEnabled);
1939
+ return {
1940
+ tools: tools.map((tool) => ({
1941
+ name: tool.metadata?.name ?? tool.filename,
1942
+ description: tool.metadata?.description ?? `${tool.filename} tool`,
1943
+ inputSchema: tool.inputSchema,
1944
+ annotations: tool.metadata?.annotations,
1945
+ payment: tool.metadata?.payment,
1946
+ discovery: tool.metadata?.discovery
1947
+ }))
1948
+ };
1949
+ });
1950
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1951
+ const tools = getTools().filter(isMcpEnabled);
1952
+ const tool = tools.find((entry) => {
1953
+ const toolName = entry.metadata?.name ?? entry.filename;
1954
+ return toolName === request.params.name;
1955
+ });
1956
+ if (!tool) {
1957
+ throw new Error(`Tool ${request.params.name} not found`);
1958
+ }
1959
+ try {
1960
+ const validatedParams = tool.schema.parse(
1961
+ request.params.arguments
1962
+ );
1963
+ const handler = tool.handler ?? createMcpAdapter({
1964
+ name: tool.metadata?.name ?? tool.filename,
1965
+ httpHandlers: toHttpHandlerMap2(tool.httpHandlers),
1966
+ ...tool.schema ? { schema: tool.schema } : {},
1967
+ ...tool.mcpConfig?.defaultMethod ? { defaultMethod: tool.mcpConfig.defaultMethod } : {}
1968
+ });
1969
+ const result = await handler(validatedParams);
1970
+ return result;
1971
+ } catch (error) {
1972
+ const message = error && error.message || String(error);
1973
+ return {
1974
+ content: [{ type: "text", text: `Error: ${message}` }],
1975
+ isError: true
1976
+ };
1977
+ }
1978
+ });
1979
+ const transport = new StdioServerTransport();
1980
+ server.connect(transport).catch((error) => {
1981
+ console.error("MCP transport error:", error);
1982
+ });
1983
+ return {
1984
+ async close() {
1985
+ await server.close();
1986
+ if (typeof transport.close === "function") {
1987
+ transport.close();
1988
+ }
1989
+ }
1990
+ };
1991
+ }
1992
+ async function loadToolDefinitions(toolsDir, projectRoot) {
1993
+ return loadAndValidateTools(toolsDir, { projectRoot });
1994
+ }
1995
+ function expandRoutes(tools) {
1996
+ const routes = [];
1997
+ tools.forEach((tool) => {
1998
+ tool.httpHandlers.forEach((handlerDef) => {
1999
+ routes.push({
2000
+ tool,
2001
+ method: handlerDef.method.toUpperCase(),
2002
+ handler: async (request) => handlerDef.handler(request)
2003
+ });
2004
+ });
2005
+ });
2006
+ return routes;
2007
+ }
2008
+ function logStartup(tools, stdio, log) {
2009
+ log(`
2010
+ Tools: ${tools.length} tool${tools.length === 1 ? "" : "s"}`);
2011
+ printToolList(tools, log);
2012
+ if (stdio) {
2013
+ const mcpTools = tools.filter(isMcpEnabled);
2014
+ const label = mcpTools.length > 0 ? `MCP stdio enabled (${mcpTools.length} tool${mcpTools.length === 1 ? "" : "s"})` : "MCP stdio enabled (no tools opted in)";
2015
+ log(`${dim}${label}${reset}`);
2016
+ }
2017
+ }
2018
+ function logReload(tools, stdio, log) {
2019
+ log(`
2020
+ Reloaded ${tools.length} tool${tools.length === 1 ? "" : "s"}`);
2021
+ printToolList(tools, log);
2022
+ if (stdio) {
2023
+ const mcpTools = tools.filter(isMcpEnabled);
2024
+ const label = mcpTools.length > 0 ? `MCP stdio enabled (${mcpTools.length} tool${mcpTools.length === 1 ? "" : "s"})` : "MCP stdio enabled (no tools opted in)";
2025
+ log(`${dim}${label}${reset}`);
2026
+ }
2027
+ }
2028
+ function printToolList(tools, log) {
2029
+ tools.forEach((tool) => {
2030
+ const name = tool.metadata?.name ?? tool.filename;
2031
+ const methods = tool.httpHandlers.map((handler) => handler.method).join(", ");
2032
+ const tags = [];
2033
+ if (tool.mcpConfig?.enabled) {
2034
+ tags.push(`${dim}[mcp]${reset}`);
2035
+ }
2036
+ if (tool.payment || tool.metadata && tool.metadata.payment) {
2037
+ tags.push(`${dim}[payments]${reset}`);
2038
+ }
2039
+ const tagSuffix = tags.length ? ` ${tags.join(" ")}` : "";
2040
+ log(` \u2022 ${name} \u2014 ${methods}${tagSuffix}`);
2041
+ });
2042
+ }
2043
+ async function handleRequest(params) {
2044
+ const { req, res, port, routes } = params;
2045
+ res.setHeader("Access-Control-Allow-Origin", "*");
2046
+ res.setHeader(
2047
+ "Access-Control-Allow-Methods",
2048
+ HTTP_METHODS.join(", ") + ", OPTIONS"
2049
+ );
2050
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
2051
+ if (req.method === "OPTIONS") {
2052
+ res.writeHead(200);
2053
+ res.end();
2054
+ return;
2055
+ }
2056
+ const method = (req.method || "GET").toUpperCase();
2057
+ const url = new URL(req.url || "/", `http://localhost:${port}`);
2058
+ const toolName = url.pathname.slice(1) || "index";
2059
+ const route = findRoute(toolName, method, routes);
2060
+ if (!route) {
2061
+ res.writeHead(404, { "Content-Type": "application/json" });
2062
+ res.end(
2063
+ JSON.stringify({
2064
+ error: `Tool not found: ${method} /${toolName}`,
2065
+ availableTools: routes.map((r) => `${r.method} /${routeName(r.tool)}`)
2066
+ })
2067
+ );
2068
+ return;
2069
+ }
2070
+ const body = await readRequestBody(req);
2071
+ const request = createWebRequest({ req, url, body });
2072
+ let response;
2073
+ try {
2074
+ response = await route.handler(request);
2075
+ } catch (error) {
2076
+ if (error instanceof PaymentRequiredError) {
2077
+ response = error.response;
2078
+ } else {
2079
+ throw error;
2080
+ }
2081
+ }
2082
+ const headers = {};
2083
+ response.headers.forEach((value, key) => {
2084
+ headers[key] = value;
2085
+ });
2086
+ res.writeHead(response.status, headers);
2087
+ if (method === "HEAD") {
2088
+ res.end();
2089
+ return;
2090
+ }
2091
+ const arrayBuffer = await response.arrayBuffer();
2092
+ res.end(Buffer.from(arrayBuffer));
2093
+ }
2094
+ function findRoute(toolName, method, routes) {
2095
+ const direct = routes.find(
2096
+ (route) => routeName(route.tool) === toolName && route.method === method
2097
+ );
2098
+ if (direct) {
2099
+ return direct;
2100
+ }
2101
+ if (method === "HEAD") {
2102
+ return routes.find(
2103
+ (route) => routeName(route.tool) === toolName && route.method === "GET"
2104
+ );
2105
+ }
2106
+ return void 0;
2107
+ }
2108
+ function routeName(tool) {
2109
+ return tool.metadata?.name ?? tool.filename;
2110
+ }
2111
+ async function readRequestBody(req) {
2112
+ const chunks = [];
2113
+ for await (const chunk of req) {
2114
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
2115
+ }
2116
+ return Buffer.concat(chunks);
2117
+ }
2118
+ function createWebRequest(params) {
2119
+ const { req, url, body } = params;
2120
+ const headers = new Headers();
2121
+ Object.entries(req.headers).forEach(([key, value]) => {
2122
+ if (value === void 0) {
2123
+ return;
2124
+ }
2125
+ if (Array.isArray(value)) {
2126
+ value.forEach((entry) => headers.append(key, entry));
2127
+ return;
2128
+ }
2129
+ headers.set(key, value);
2130
+ });
2131
+ const method = (req.method || "GET").toUpperCase();
2132
+ const init = {
2133
+ method,
2134
+ headers
2135
+ };
2136
+ if (body.length > 0 && method !== "GET" && method !== "HEAD") {
2137
+ init.body = body.toString();
2138
+ }
2139
+ return new Request(url, init);
2140
+ }
2141
+ function toHttpHandlerMap2(handlers) {
2142
+ return handlers.reduce(
2143
+ (acc, handler) => {
2144
+ acc[handler.method.toUpperCase()] = handler.handler;
2145
+ return acc;
2146
+ },
2147
+ {}
2148
+ );
2149
+ }
2150
+ function isMcpEnabled(tool) {
2151
+ return Boolean(tool.mcpConfig?.enabled);
2152
+ }
2153
+ async function generateMetadataCommand(options) {
2154
+ const startTimestamp = timestamp2();
2155
+ console.log(`[${startTimestamp}] Generating OpenTool metadata...`);
2156
+ try {
2157
+ const result = await generateMetadata(options);
2158
+ const endTimestamp = timestamp2();
2159
+ console.log(`[${endTimestamp}] Metadata generation completed successfully!`);
2160
+ console.log(`Output file: ${result.outputPath}`);
2161
+ console.log(`Spec version: ${result.metadata.metadataSpecVersion}`);
2162
+ console.log(`Tools included: ${result.tools.length}`);
2163
+ if (result.defaultsApplied.length > 0) {
2164
+ console.log("Applied defaults:");
2165
+ for (const entry of result.defaultsApplied) {
2166
+ console.log(` \u2022 ${entry}`);
2167
+ }
2168
+ }
2169
+ } catch (error) {
2170
+ const endTimestamp = timestamp2();
2171
+ console.error(`[${endTimestamp}] Metadata generation failed:`, error);
2172
+ process.exit(1);
2173
+ }
2174
+ }
2175
+ async function generateMetadata(options) {
2176
+ const toolsDir = path5.resolve(options.input);
2177
+ if (!fs4.existsSync(toolsDir)) {
2178
+ throw new Error(`Tools directory not found: ${toolsDir}`);
2179
+ }
2180
+ const projectRoot = path5.dirname(toolsDir);
2181
+ const tools = await loadAndValidateTools(toolsDir, { projectRoot });
2182
+ const { metadata, defaultsApplied } = await buildMetadataArtifact({
2183
+ projectRoot,
2184
+ tools
2185
+ });
2186
+ const outputPath = options.output ? path5.resolve(options.output) : path5.join(projectRoot, "metadata.json");
2187
+ fs4.writeFileSync(outputPath, JSON.stringify(metadata, null, 2));
2188
+ return {
2189
+ metadata,
2190
+ defaultsApplied,
2191
+ tools,
2192
+ outputPath
2193
+ };
2194
+ }
2195
+ function timestamp2() {
2196
+ return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
2197
+ }
2198
+
2199
+ // src/cli/index.ts
2200
+ program.name("opentool").description("OpenTool CLI for building and developing serverless MCP tools").version("1.0.0");
2201
+ program.command("dev").description("Start HTTP dev server (optional MCP stdio)").option("-i, --input <dir>", "Input directory containing tools", "tools").option("-p, --port <port>", "Port to listen on", "7000").option("--stdio", "Expose MCP stdio transport", false).option("--no-watch", "Disable file watching").action((cmdOptions) => {
2202
+ devCommand({
2203
+ input: cmdOptions.input,
2204
+ port: Number(cmdOptions.port ?? 7e3),
2205
+ watch: cmdOptions.watch,
2206
+ stdio: cmdOptions.stdio
2207
+ });
2208
+ });
2209
+ program.command("build").description("Build tools for deployment").option("-i, --input <dir>", "Input directory containing tools", "tools").option("-o, --output <dir>", "Output directory for built tools", "dist").option("--name <name>", "Server name", "opentool-server").option("--version <version>", "Server version", "1.0.0").action(buildCommand);
2210
+ program.command("validate").description("Validate metadata for registry submission").option("-i, --input <dir>", "Input directory containing tools", "tools").action(validateCommand);
2211
+ program.command("validate-full").description("Full validation of tools and metadata").option("-i, --input <dir>", "Input directory containing tools", "tools").action(validateFullCommand);
2212
+ program.command("metadata").description("Generate OpenTool metadata JSON without building").option("-i, --input <dir>", "Input directory containing tools", "tools").option(
2213
+ "-o, --output <file>",
2214
+ "Output file path for metadata.json",
2215
+ "metadata.json"
2216
+ ).option("--name <name>", "Server name", "opentool-server").option("--version <version>", "Server version", "1.0.0").action(generateMetadataCommand);
2217
+ program.parse();
2218
+
2219
+ export { buildCommand, buildProject, devCommand, generateMetadata, generateMetadataCommand, loadAndValidateTools, validateCommand, validateFullCommand };
2220
+ //# sourceMappingURL=index.js.map
70
2221
  //# sourceMappingURL=index.js.map