opentool 0.6.4 → 0.7.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.
package/README.md CHANGED
@@ -113,6 +113,37 @@ npx mcp-inspector --config inspector.json --server opentool-dev
113
113
 
114
114
  Copy `.env.example` to `.env` and add your credentials if you're using wallet/payment features. The inspector starts `opentool dev` automatically, so you only need one terminal. Only tools with `mcp = { enabled: true }` show up in the inspector - HTTP-only tools keep running on localhost.
115
115
 
116
+ ### Quick x402 test with curl
117
+
118
+ 1. Start the dev server against the example tools:
119
+
120
+ ```bash
121
+ npx opentool dev --input examples/full-metadata/tools
122
+ ```
123
+
124
+ 2. Trigger the paywall and inspect the returned payment requirements:
125
+
126
+ ```bash
127
+ curl -i \
128
+ -X POST http://localhost:7000/premium-report \
129
+ -H "content-type: application/json" \
130
+ -d '{"symbol":"BTC"}'
131
+ ```
132
+
133
+ The response includes a `402 Payment Required` status and JSON body with an `x402.accepts[0]` object describing the payment request.
134
+
135
+ 3. Submit a follow-up request with an `X-PAYMENT` header produced by your x402 facilitator (for example, by using the Coinbase [x402](https://github.com/coinbase/x402) tooling or your own signing flow):
136
+
137
+ ```bash
138
+ curl -i \
139
+ -X POST http://localhost:7000/premium-report \
140
+ -H "content-type: application/json" \
141
+ -H "X-PAYMENT: ${X402_HEADER}" \
142
+ -d '{"symbol":"BTC"}'
143
+ ```
144
+
145
+ Replace `${X402_HEADER}` with the base64-encoded payment payload returned by your facilitator’s `/verify` or `/pay` workflow. If the payment is valid the server responds with `200 OK`; otherwise it returns a new `402` with failure details.
146
+
116
147
  ### 5. Build for deployment
117
148
 
118
149
  ```bash
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { h as Metadata, I as InternalToolDefinition } from '../validate-BLlooEBK.js';
3
- export { G as GenerateMetadataOptions, i as GenerateMetadataResult, V as ValidateOptions, g as generateMetadata, a as generateMetadataCommand, l as loadAndValidateTools, v as validateCommand, j as validateFullCommand } from '../validate-BLlooEBK.js';
2
+ import { h as Metadata, I as InternalToolDefinition } from '../validate-CqB2Juma.js';
3
+ export { G as GenerateMetadataOptions, i as GenerateMetadataResult, V as ValidateOptions, g as generateMetadata, a as generateMetadataCommand, l as loadAndValidateTools, v as validateCommand, j as validateFullCommand } from '../validate-CqB2Juma.js';
4
4
  import 'zod';
5
- import '../index-D3DaM5Rs.js';
5
+ import '../index-D_bCF2Bf.js';
6
6
 
7
7
  interface BuildOptions {
8
8
  input: string;
@@ -15,6 +15,7 @@ interface BuildArtifacts {
15
15
  defaultsApplied: string[];
16
16
  tools: InternalToolDefinition[];
17
17
  compiledTools: CompiledToolArtifact[];
18
+ workflowBundles: WorkflowBundleArtifact | null;
18
19
  }
19
20
  interface CompiledToolArtifact {
20
21
  name: string;
@@ -25,6 +26,15 @@ interface CompiledToolArtifact {
25
26
  defaultMcpMethod?: string;
26
27
  hasWallet: boolean;
27
28
  }
29
+ interface WorkflowBundleArtifact {
30
+ sourceDir: string;
31
+ outputDir: string;
32
+ stepsBundlePath: string;
33
+ workflowsBundlePath: string;
34
+ webhookBundlePath: string;
35
+ clientBundlePath?: string;
36
+ manifestPath?: string;
37
+ }
28
38
  declare function buildCommand(options: BuildOptions): Promise<void>;
29
39
  declare function buildProject(options: BuildOptions): Promise<BuildArtifacts>;
30
40
 
package/dist/cli/index.js CHANGED
@@ -7,7 +7,7 @@ import { build } from 'esbuild';
7
7
  import { z } from 'zod';
8
8
  import { createRequire } from 'module';
9
9
  import { fileURLToPath, pathToFileURL } from 'url';
10
- import { zodToJsonSchema } from 'zod-to-json-schema';
10
+ import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
11
11
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
12
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
13
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
@@ -95,7 +95,7 @@ var PaymentConfigSchema = z.object({
95
95
  plain402: z.boolean().optional(),
96
96
  acceptedMethods: z.array(z.union([z.literal("x402"), z.literal("402")])).optional(),
97
97
  acceptedCurrencies: z.array(z.string()).optional(),
98
- chainIds: z.array(z.number().int()).optional(),
98
+ chains: z.array(z.union([z.string(), z.number()])).optional(),
99
99
  facilitator: z.string().optional()
100
100
  }).strict();
101
101
  var DiscoveryMetadataSchema = z.object({
@@ -103,9 +103,9 @@ var DiscoveryMetadataSchema = z.object({
103
103
  category: z.string().optional(),
104
104
  useCases: z.array(z.string()).optional(),
105
105
  capabilities: z.array(z.string()).optional(),
106
- requirements: z.record(z.any()).optional(),
107
- pricing: z.record(z.any()).optional(),
108
- compatibility: z.record(z.any()).optional(),
106
+ requirements: z.record(z.string(), z.any()).optional(),
107
+ pricing: z.record(z.string(), z.any()).optional(),
108
+ compatibility: z.record(z.string(), z.any()).optional(),
109
109
  documentation: z.union([z.string(), z.array(z.string())]).optional()
110
110
  }).catchall(z.any());
111
111
  var ToolMetadataOverridesSchema = z.object({
@@ -148,9 +148,9 @@ var AuthoredMetadataSchema = z.object({
148
148
  keywords: z.array(z.string()).optional(),
149
149
  useCases: z.array(z.string()).optional(),
150
150
  capabilities: z.array(z.string()).optional(),
151
- requirements: z.record(z.any()).optional(),
152
- pricing: z.record(z.any()).optional(),
153
- compatibility: z.record(z.any()).optional(),
151
+ requirements: z.record(z.string(), z.any()).optional(),
152
+ pricing: z.record(z.string(), z.any()).optional(),
153
+ compatibility: z.record(z.string(), z.any()).optional(),
154
154
  chains: z.array(z.union([z.string(), z.number()])).optional()
155
155
  }).catchall(z.any());
156
156
  var MetadataSchema = z.object({
@@ -409,7 +409,7 @@ function resolvePayment(authored, defaults) {
409
409
  plain402: acceptedMethods.includes("402"),
410
410
  acceptedMethods,
411
411
  acceptedCurrencies: Array.isArray(pricing.acceptedCurrencies) ? pricing.acceptedCurrencies : ["USDC"],
412
- chainIds: Array.isArray(pricing.chainIds) ? pricing.chainIds : [8453]
412
+ chains: Array.isArray(pricing.chains) ? pricing.chains : [8453]
413
413
  };
414
414
  }
415
415
  function buildDiscovery(authored) {
@@ -1667,6 +1667,10 @@ async function buildProject(options) {
1667
1667
  projectRoot,
1668
1668
  outputDir
1669
1669
  });
1670
+ const workflowBundles = await buildWorkflowsIfPresent({
1671
+ projectRoot,
1672
+ outputDir
1673
+ });
1670
1674
  const shouldBuildMcpServer = compiledTools.some((artifact) => artifact.mcpEnabled);
1671
1675
  if (shouldBuildMcpServer) {
1672
1676
  await writeMcpServer({
@@ -1684,7 +1688,8 @@ async function buildProject(options) {
1684
1688
  metadata,
1685
1689
  defaultsApplied,
1686
1690
  tools,
1687
- compiledTools
1691
+ compiledTools,
1692
+ workflowBundles
1688
1693
  };
1689
1694
  }
1690
1695
  async function emitTools(tools, config) {
@@ -1843,6 +1848,18 @@ function logBuildSummary(artifacts, options) {
1843
1848
  console.log(` - ${tool.name} [${methods}]${walletBadge}`);
1844
1849
  });
1845
1850
  console.log(" \u2022 metadata.json (registry artifact)");
1851
+ if (artifacts.workflowBundles) {
1852
+ console.log(" \u2022 .well-known/workflow/v1/ (workflow bundles)");
1853
+ console.log(" - flow.js");
1854
+ console.log(" - step.js");
1855
+ console.log(" - webhook.js");
1856
+ if (artifacts.workflowBundles.clientBundlePath) {
1857
+ console.log(" - client.js");
1858
+ }
1859
+ if (artifacts.workflowBundles.manifestPath) {
1860
+ console.log(" - manifest.json");
1861
+ }
1862
+ }
1846
1863
  if (artifacts.defaultsApplied.length > 0) {
1847
1864
  console.log("\nDefaults applied during metadata synthesis:");
1848
1865
  artifacts.defaultsApplied.forEach((entry) => console.log(` \u2022 ${entry}`));
@@ -1851,6 +1868,155 @@ function logBuildSummary(artifacts, options) {
1851
1868
  console.log("\n\u2139\uFE0F MCP adapter skipped (no tools opted in)");
1852
1869
  }
1853
1870
  }
1871
+ async function buildWorkflowsIfPresent(options) {
1872
+ const workflowsDir = options.workflowsDir ?? path5.join(options.projectRoot, "workflows");
1873
+ if (!fs4.existsSync(workflowsDir)) {
1874
+ return null;
1875
+ }
1876
+ if (!hasWorkflowSourceFiles(workflowsDir)) {
1877
+ return null;
1878
+ }
1879
+ const nodeVersion = process.versions?.node ?? "0.0.0";
1880
+ const nodeMajor = Number(nodeVersion.split(".")[0] ?? 0);
1881
+ if (!Number.isFinite(nodeMajor) || nodeMajor < 22) {
1882
+ console.warn(
1883
+ `[${timestamp()}] Workflow bundles skipped (requires Node >= 22, current ${nodeVersion})`
1884
+ );
1885
+ return null;
1886
+ }
1887
+ const { BaseBuilder } = await import('@workflow/cli/dist/lib/builders/base-builder.js');
1888
+ class OpenToolWorkflowBuilder extends BaseBuilder {
1889
+ async build() {
1890
+ const inputFiles = await this.getInputFiles();
1891
+ const tsConfig = await this.getTsConfigOptions();
1892
+ const shared = {
1893
+ inputFiles,
1894
+ ...tsConfig.baseUrl ? { tsBaseUrl: tsConfig.baseUrl } : {},
1895
+ ...tsConfig.paths ? { tsPaths: tsConfig.paths } : {}
1896
+ };
1897
+ await this.buildStepsBundle(shared);
1898
+ await this.buildWorkflowsBundle(shared);
1899
+ await this.buildWebhookRoute();
1900
+ await this.buildClientLibrary();
1901
+ }
1902
+ async buildStepsBundle(options2) {
1903
+ console.log(
1904
+ "Creating OpenTool workflow steps bundle at",
1905
+ this.config.stepsBundlePath
1906
+ );
1907
+ const stepsBundlePath2 = path5.resolve(
1908
+ this.config.workingDir,
1909
+ this.config.stepsBundlePath
1910
+ );
1911
+ await fs4.promises.mkdir(path5.dirname(stepsBundlePath2), { recursive: true });
1912
+ await this.createStepsBundle({
1913
+ outfile: stepsBundlePath2,
1914
+ ...options2
1915
+ });
1916
+ }
1917
+ async buildWorkflowsBundle(options2) {
1918
+ console.log(
1919
+ "Creating OpenTool workflow bundle at",
1920
+ this.config.workflowsBundlePath
1921
+ );
1922
+ const workflowBundlePath = path5.resolve(
1923
+ this.config.workingDir,
1924
+ this.config.workflowsBundlePath
1925
+ );
1926
+ await fs4.promises.mkdir(path5.dirname(workflowBundlePath), {
1927
+ recursive: true
1928
+ });
1929
+ await this.createWorkflowsBundle({
1930
+ outfile: workflowBundlePath,
1931
+ bundleFinalOutput: false,
1932
+ ...options2
1933
+ });
1934
+ }
1935
+ async buildWebhookRoute() {
1936
+ console.log(
1937
+ "Creating OpenTool workflow webhook bundle at",
1938
+ this.config.webhookBundlePath
1939
+ );
1940
+ const webhookBundlePath2 = path5.resolve(
1941
+ this.config.workingDir,
1942
+ this.config.webhookBundlePath
1943
+ );
1944
+ await fs4.promises.mkdir(path5.dirname(webhookBundlePath2), {
1945
+ recursive: true
1946
+ });
1947
+ await this.createWebhookBundle({ outfile: webhookBundlePath2 });
1948
+ }
1949
+ }
1950
+ const relativeSourceDir = path5.relative(options.projectRoot, workflowsDir) || ".";
1951
+ const outputBase = path5.join(
1952
+ options.outputDir,
1953
+ ".well-known",
1954
+ "workflow",
1955
+ "v1"
1956
+ );
1957
+ const stepsBundlePath = path5.join(outputBase, "step.js");
1958
+ const workflowsBundlePath = path5.join(outputBase, "flow.js");
1959
+ const webhookBundlePath = path5.join(outputBase, "webhook.js");
1960
+ const manifestPath = path5.join(outputBase, "manifest.json");
1961
+ const builder = new OpenToolWorkflowBuilder({
1962
+ workingDir: options.projectRoot,
1963
+ dirs: [relativeSourceDir],
1964
+ buildTarget: "standalone",
1965
+ stepsBundlePath,
1966
+ workflowsBundlePath,
1967
+ webhookBundlePath,
1968
+ ...{},
1969
+ workflowManifestPath: manifestPath,
1970
+ externalPackages: [
1971
+ "workflow",
1972
+ "workflow/internal/builtins",
1973
+ "workflow/internal/private",
1974
+ "workflow/runtime",
1975
+ "workflow/api"
1976
+ ]
1977
+ });
1978
+ console.log(
1979
+ `[${timestamp()}] Building workflows from ${workflowsDir} -> ${outputBase}`
1980
+ );
1981
+ await builder.build();
1982
+ return {
1983
+ sourceDir: workflowsDir,
1984
+ outputDir: outputBase,
1985
+ stepsBundlePath,
1986
+ workflowsBundlePath,
1987
+ webhookBundlePath,
1988
+ ...{},
1989
+ manifestPath
1990
+ };
1991
+ }
1992
+ function hasWorkflowSourceFiles(directory) {
1993
+ const entries = fs4.readdirSync(directory, { withFileTypes: true });
1994
+ for (const entry of entries) {
1995
+ if (entry.isDirectory()) {
1996
+ if (hasWorkflowSourceFiles(path5.join(directory, entry.name))) {
1997
+ return true;
1998
+ }
1999
+ continue;
2000
+ }
2001
+ if (entry.isFile()) {
2002
+ const extension = path5.extname(entry.name).toLowerCase();
2003
+ if (WORKFLOW_SOURCE_EXTENSIONS.has(extension)) {
2004
+ return true;
2005
+ }
2006
+ }
2007
+ }
2008
+ return false;
2009
+ }
2010
+ var WORKFLOW_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
2011
+ ".ts",
2012
+ ".tsx",
2013
+ ".js",
2014
+ ".jsx",
2015
+ ".mjs",
2016
+ ".cjs",
2017
+ ".mts",
2018
+ ".cts"
2019
+ ]);
1854
2020
  function timestamp() {
1855
2021
  return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
1856
2022
  }