opentool 0.6.1 → 0.6.3

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
@@ -3,50 +3,37 @@
3
3
  [![npm version](https://badge.fury.io/js/opentool.svg)](https://badge.fury.io/js/opentool)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A comprehensive TypeScript framework for building, deploying, and monetizing serverless MCP (Model Context Protocol) tools with integrated AI, blockchain wallets, and crypto payments.
6
+ Build serverless TypeScript tools that work with AI assistants, handle crypto payments, and deploy to AWS Lambda automatically.
7
7
 
8
8
  **For LLMs/AI Code Generation:** [`dist/opentool-context.ts`](./scripts/build-context.ts)
9
9
 
10
- ## Overview
10
+ ## What is it?
11
11
 
12
- OpenTool is a complete platform for creating intelligent, payment-enabled tools that run on AWS Lambda. It combines:
12
+ OpenTool lets you write simple TypeScript functions that can be called by other agents, monetized with crypto payments, and deployed as serverless functions. It handles the boring stuff like:
13
13
 
14
- - **MCP Protocol** - Full Model Context Protocol implementation with stdio and HTTP transports
15
- - **AI Integration** - Built-in AI client with text generation, streaming, and tool calling capabilities
16
- - **Wallet System** - Multi-chain blockchain wallet support with Turnkey and private key providers
17
- - **Payment Infrastructure** - Crypto payment integration for monetizing tools on-chain
18
- - **Serverless Deployment** - Automatic AWS Lambda deployment via [OpenPond](https://openpond.ai)
19
- - **Type Safety** - Full TypeScript support with Zod schema validation
14
+ - Type validation with Zod schemas
15
+ - AI client integration (OpenAI, Anthropic, etc.)
16
+ - Multi-chain wallet support (Ethereum, Base, Arbitrum, etc.)
17
+ - Automatic AWS Lambda deployment via [OpenPond](https://openpond.ai)
18
+ - Payment infrastructure for on-chain tool monetization
20
19
 
21
- ## Features
22
-
23
- ### Core Framework
24
-
25
- - **Serverless-first**: Tools automatically deploy to AWS Lambda with Function URLs
26
- - **Type-safe**: Full TypeScript support with Zod schema validation and automatic JSON schema generation
27
- - **CLI Tools**: Build, develop, validate, and generate metadata with comprehensive CLI
28
- - **Hot Reloading**: Development server with optional watch mode for rapid iteration
29
- - **MCP Compatible**: Works with any MCP client (Claude Desktop, MCP Inspector, etc.)
30
-
31
- ### AI Capabilities
32
-
33
- - **Multi-Model Support**: OpenAI, Anthropic, and compatible providers
34
- - **Text Generation**: Simple and streaming text generation with tool calling
35
- - **Built-in Tools**: Web search and custom tool integration
36
- - **Model Management**: Automatic model normalization and capability detection
37
-
38
- ### Blockchain & Payments
20
+ ## Recent Updates
39
21
 
40
- - **Multi-Chain Wallets**: Support for Ethereum, Base, Optimism, Arbitrum, Polygon, and more
41
- - **Provider Flexibility**: Private key or Turnkey-managed signing
42
- - **Crypto Payments**: On-chain payment integration with ERC-20 token support
43
- - **Token Registry**: Built-in registry for common tokens (USDC, USDT, DAI, etc.)
22
+ - **Selective MCP mode** - tools now support `mcp = { enabled: true }` to enable MCP clients on a per-tool basis
23
+ - **Context bundling** - generates consolidated context files for AI code generation (see `dist/opentool-context.ts`)
24
+ - **Default bundling enabled** - tools now bundle by default for cleaner deployments
25
+ - **Improved CLI** - better validation and metadata generation commands
44
26
 
45
- ### Discovery & Metadata
27
+ ## Features
46
28
 
47
- - **Three-Tier Metadata**: Smart defaults, enhanced metadata, and per-tool overrides
48
- - **On-Chain Registration**: Metadata formatted for blockchain discovery
49
- - **Rich Annotations**: Icons, categories, tags, and custom branding
29
+ - **TypeScript-first** with Zod validation and auto-generated JSON schemas
30
+ - **Serverless by default** - deploys to AWS Lambda with Function URLs
31
+ - **MCP support** - works with Claude Desktop, MCP Inspector, or any MCP client
32
+ - **Built-in AI client** for OpenAI, Anthropic, and compatible providers
33
+ - **Multi-chain wallets** - Ethereum, Base, Arbitrum, Polygon, etc.
34
+ - **Crypto payments** - monetize tools with ERC-20 tokens (USDC, USDT, DAI)
35
+ - **CLI tools** for building, validating, and local dev with watch mode
36
+ - **Context bundling** - generates consolidated context files for AI code generation
50
37
 
51
38
  ## Installation
52
39
 
@@ -102,33 +89,44 @@ npx opentool validate
102
89
  npx opentool dev
103
90
  ```
104
91
 
105
- ### MCP Inspector
92
+ ### 4. Enable MCP mode (optional)
106
93
 
107
- The `examples/full-metadata` project includes an `inspector.json` preset so you can exercise MCP tools with the official MCP Inspector:
94
+ By default, tools are HTTP-only. Want them accessible via MCP clients like Claude Desktop? Just add this to your tool file:
95
+
96
+ ```typescript
97
+ // tools/greet.ts
98
+ export const mcp = {
99
+ enabled: true, // Now works with Claude Desktop, MCP Inspector, etc.
100
+ };
101
+ ```
102
+
103
+ Tools without this export stay HTTP-only, which is useful when you want selective access. Mix and match as needed.
104
+
105
+ ### Testing with MCP Inspector
106
+
107
+ The `examples/full-metadata` project has an `inspector.json` config ready to go:
108
108
 
109
109
  ```bash
110
110
  cd examples/full-metadata
111
111
  npx mcp-inspector --config inspector.json --server opentool-dev
112
112
  ```
113
113
 
114
- Before starting the inspector, copy `examples/full-metadata/.env.example` to `examples/full-metadata/.env` and populate the Turnkey, 0x, and Alchemy secrets with your own credentials. The actual `.env` file is git-ignored so you can keep real keys out of version control.
115
-
116
- The inspector spawns `opentool dev --stdio --no-watch --input tools`, so you don’t need a second terminal. Only tools that export `mcp = { enabled: true }` (for example `mcp_ping`) appear in the inspector’s tool list; HTTP-only tools like `calculate` and `hello` keep responding on the local HTTP port.
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.
117
115
 
118
- ### 4. Build for deployment
116
+ ### 5. Build for deployment
119
117
 
120
118
  ```bash
121
119
  # Build tools for Lambda deployment
122
120
  npx opentool build
123
121
  ```
124
122
 
125
- ### 5. Deploy to OpenPond
123
+ ### 6. Deploy to OpenPond
126
124
 
127
125
  Create an account on [OpenPond](https://openpond.ai) and create a new project.
128
126
 
129
127
  Add your project to the OpenPond project and connect it to your GitHub repository.
130
128
 
131
- OpenPond will automatically detect the `opentool` dependency and deploy your tools to AWS Lambda using the UI.
129
+ OpenPond will automatically detect the `opentool` dependency and deploy your tools to AWS Lambda.
132
130
 
133
131
  ## CLI Commands
134
132
 
@@ -171,7 +169,7 @@ Options:
171
169
 
172
170
  ### Generate Metadata
173
171
 
174
- Generate OpenTool metadata JSON without building:
172
+ Generate `metadata.json` without building:
175
173
 
176
174
  ```bash
177
175
  npx opentool metadata [options]
@@ -183,33 +181,37 @@ Options:
183
181
  --version <version> Server version (default: "1.0.0")
184
182
  ```
185
183
 
186
- This command generates the `metadata.json` file that contains all the information needed for on-chain registration and discovery, including tool schemas, payment configurations, and discovery metadata. It's useful when you need to inspect or share the metadata without performing a full build.
184
+ Generates the metadata file with tool schemas, payment configs, and discovery info. Useful for inspecting or sharing metadata without a full build.
187
185
 
188
186
  ## Tool Definition
189
187
 
190
- Each tool is defined by exporting:
191
-
192
- 1. **schema**: Zod schema for input validation
193
- 2. **metadata**: Tool information and MCP annotations
194
- 3. **HTTP handler**: Async function that implements the tool logic (e.g., POST, GET, PUT, DELETE)
188
+ Tools are just TypeScript files with a few exports:
195
189
 
196
190
  ```typescript
197
191
  import { z } from "zod";
198
192
 
193
+ // 1. Schema for input validation
199
194
  export const schema = z.object({
200
- // Define your input parameters here
195
+ input: z.string().describe("Some input parameter"),
201
196
  });
202
197
 
198
+ // 2. Metadata
203
199
  export const metadata = {
204
200
  name: "my_tool",
205
- description: "Description of what this tool does",
201
+ description: "What this tool does",
202
+ };
203
+
204
+ // 3. Optional: enable MCP mode
205
+ export const mcp = {
206
+ enabled: true, // Makes it work with Claude Desktop, etc.
206
207
  };
207
208
 
209
+ // 4. Handler (POST, GET, PUT, DELETE, etc.)
208
210
  export async function POST(request: Request) {
209
211
  const payload = await request.json();
210
212
  const params = schema.parse(payload);
211
213
 
212
- // Implement your tool logic here
214
+ // Your tool logic here
213
215
  return Response.json({
214
216
  result: "Tool response",
215
217
  });
@@ -218,7 +220,7 @@ export async function POST(request: Request) {
218
220
 
219
221
  ## Error Handling
220
222
 
221
- Return appropriate HTTP status codes and error messages in your response:
223
+ Just return standard HTTP responses:
222
224
 
223
225
  ```typescript
224
226
  export async function POST(request: Request) {
@@ -235,72 +237,66 @@ export async function POST(request: Request) {
235
237
 
236
238
  ## Local Development
237
239
 
238
- The development server runs your tools locally using the MCP protocol over stdio. You can:
240
+ Run `npx opentool dev` to test your tools locally. It runs them via stdio (for MCP clients) or HTTP (for direct API calls). Good for:
239
241
 
240
- 1. Test individual tools
241
- 2. Validate schemas
242
- 3. Check tool responses
243
- 4. Debug issues before deployment
242
+ - Testing tool logic
243
+ - Validating schemas
244
+ - Debugging before deployment
244
245
 
245
246
  ## Deployment
246
247
 
247
- When you push a project with `opentool` dependency to GitHub and connect it to OpenPond:
248
+ Push your repo to GitHub and connect it to [OpenPond](https://openpond.ai):
248
249
 
249
- 1. **Detection**: OpenPond detects the `opentool` package
250
- 2. **Routing**: Project is routed to AWS Lambda deployment
251
- 3. **Build**: Tools are built using `npx opentool build`
252
- 4. **Deploy**: Lambda function is created with Function URLs
253
- 5. **Ready**: Your tools are available as serverless MCP servers!
250
+ 1. OpenPond detects the `opentool` dependency
251
+ 2. Runs `npx opentool build`
252
+ 3. Deploys to AWS Lambda with Function URLs
253
+ 4. Done - your tools are live
254
254
 
255
255
  ## Examples
256
256
 
257
- See the `examples/` directory for a comprehensive example:
258
-
259
- - **`examples/full-metadata/`** - Metadata configuration with payment and discovery features
257
+ Check `examples/full-metadata/` for a complete example with payment and discovery features.
260
258
 
261
259
  ### Testing Examples Locally
262
260
 
263
- To test the examples using the local development version:
264
-
265
261
  ```bash
266
- # Build the OpenTool package
262
+ # Build and link the OpenTool package
267
263
  npm run build
268
264
  npm link
269
265
 
270
- # Test the full metadata example
266
+ # Test the example
271
267
  cd examples/full-metadata
272
268
  npm link opentool
273
269
  npm run build
274
270
 
275
- # Examine generated output
271
+ # Check the output
276
272
  cat dist/metadata.json
277
273
 
278
274
  # Test the MCP server
279
275
  echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | node dist/mcp-server.js
280
276
 
281
- # Quick regression helpers from the repo root
282
- npm run examples:build # Build full metadata example (CJS+ESM)
283
- npm run examples:validate # Validate example metadata and tools
284
- npm run examples:metadata # Regenerate metadata.json without rebuilding tools
277
+ # Or from repo root:
278
+ npm run examples:build # Build example (CJS+ESM)
279
+ npm run examples:validate # Validate metadata and tools
280
+ npm run examples:metadata # Regenerate metadata.json
285
281
  ```
286
282
 
287
283
  ## Metadata System
288
284
 
289
- OpenTool features a sophisticated **three-tier metadata system**:
285
+ OpenTool has three levels of metadata config:
290
286
 
291
- 1. **Smart Defaults** - Zero configuration, automatic generation from `package.json`
292
- 2. **Enhanced Metadata** - Optional `metadata.ts` for custom branding and crypto payments
293
- 3. **Full Control** - Tool-level overrides for rich discovery metadata
287
+ 1. **Default** - pulls from your `package.json` automatically
288
+ 2. **Project-level** - add a `metadata.ts` file for branding, payments, etc.
289
+ 3. **Tool-level** - override metadata per tool
294
290
 
295
- See [`METADATA.md`](./METADATA.md) for the complete guide to configuring metadata for on-chain registration and payments.
291
+ See [`METADATA.md`](./METADATA.md) for details on configuring metadata for on-chain registration and payments.
296
292
 
297
- ## Future Work
293
+ ## What's Next
298
294
 
299
- - Explore an esbuild-powered watch mode that keeps metadata and tool artifacts up to date for the dev server. This remains on the follow-up list once the new pipeline settles.
295
+ - Better watch mode that keeps metadata and tool artifacts synced during dev
300
296
 
301
297
  ## Contributing
302
298
 
303
- We welcome contributions! Please see our [Contributing Guide](https://github.com/openpond/opentool/blob/master/CONTRIBUTING.md) for details.
299
+ Contributions welcome! See the [Contributing Guide](https://github.com/openpond/opentool/blob/master/CONTRIBUTING.md).
304
300
 
305
301
  ## License
306
302
 
package/dist/cli/index.js CHANGED
@@ -33,14 +33,13 @@ async function transpileWithEsbuild(options) {
33
33
  const buildOptions = {
34
34
  entryPoints: options.entryPoints,
35
35
  outdir: tempBase,
36
- bundle: false,
36
+ bundle: options.bundle ?? false,
37
37
  format: options.format,
38
38
  platform: "node",
39
39
  target: "node20",
40
40
  logLevel: "warning",
41
41
  sourcesContent: false,
42
42
  sourcemap: false,
43
- packages: "external",
44
43
  loader: {
45
44
  ".ts": "ts",
46
45
  ".tsx": "tsx",
@@ -49,12 +48,19 @@ async function transpileWithEsbuild(options) {
49
48
  ".js": "js",
50
49
  ".jsx": "jsx",
51
50
  ".mjs": "js",
52
- ".cjs": "js"
51
+ ".cjs": "js",
52
+ ".json": "json"
53
53
  },
54
54
  metafile: false,
55
55
  allowOverwrite: true,
56
56
  absWorkingDir: projectRoot
57
57
  };
58
+ if (options.external && options.external.length > 0) {
59
+ buildOptions.external = options.external;
60
+ }
61
+ if (!buildOptions.bundle) {
62
+ buildOptions.packages = "external";
63
+ }
58
64
  if (tsconfig) {
59
65
  buildOptions.tsconfig = tsconfig;
60
66
  }
@@ -1361,7 +1367,9 @@ async function loadAndValidateTools(toolsDir, options = {}) {
1361
1367
  entryPoints,
1362
1368
  projectRoot,
1363
1369
  format: "esm",
1364
- outDir: tempDir
1370
+ outDir: tempDir,
1371
+ bundle: true,
1372
+ external: ["opentool", "opentool/*"]
1365
1373
  });
1366
1374
  const tools = [];
1367
1375
  try {
@@ -1686,7 +1694,8 @@ async function emitTools(tools, config) {
1686
1694
  entryPoints,
1687
1695
  projectRoot: config.projectRoot,
1688
1696
  format: "cjs",
1689
- outDir: toolsOutDir
1697
+ outDir: toolsOutDir,
1698
+ bundle: true
1690
1699
  });
1691
1700
  const compiled = tools.map((tool) => {
1692
1701
  if (!tool.sourcePath) {
@@ -1866,13 +1875,21 @@ async function devCommand(options) {
1866
1875
  let routes = expandRoutes(toolDefinitions);
1867
1876
  const stdioController = enableStdio ? await startMcpServer(() => toolDefinitions) : null;
1868
1877
  if (watch2) {
1869
- fs4.watch(toolsDir, async (_eventType, filename) => {
1870
- if (filename && !/\.(ts|js|mjs|cjs|tsx|jsx)$/.test(filename)) {
1878
+ const reloadableExtensions = /\.(ts|js|mjs|cjs|tsx|jsx)$/i;
1879
+ const tempDir = path5.join(toolsDir, ".opentool-temp");
1880
+ const watchTargets = /* @__PURE__ */ new Set([toolsDir]);
1881
+ if (projectRoot !== toolsDir) {
1882
+ watchTargets.add(projectRoot);
1883
+ }
1884
+ let reloading = false;
1885
+ const scheduleReload = async (changedPath) => {
1886
+ if (reloading) {
1871
1887
  return;
1872
1888
  }
1889
+ reloading = true;
1873
1890
  log(
1874
1891
  `${dim}
1875
- Detected change in ${filename ?? "tools directory"}, reloading...${reset}`
1892
+ Detected change in ${changedPath ?? "tools directory"}, reloading...${reset}`
1876
1893
  );
1877
1894
  try {
1878
1895
  toolDefinitions = await loadToolDefinitions(toolsDir, projectRoot);
@@ -1880,8 +1897,24 @@ Detected change in ${filename ?? "tools directory"}, reloading...${reset}`
1880
1897
  logReload(toolDefinitions, enableStdio, log);
1881
1898
  } catch (error) {
1882
1899
  console.error("Failed to reload tools:", error);
1900
+ } finally {
1901
+ reloading = false;
1883
1902
  }
1884
- });
1903
+ };
1904
+ for (const target of watchTargets) {
1905
+ fs4.watch(target, async (_eventType, rawFilename) => {
1906
+ const filename = rawFilename?.toString();
1907
+ if (filename && !reloadableExtensions.test(filename)) {
1908
+ return;
1909
+ }
1910
+ const fullPath = filename ? path5.join(target, filename) : void 0;
1911
+ if (fullPath && fullPath.startsWith(tempDir)) {
1912
+ return;
1913
+ }
1914
+ const displayPath = fullPath ? path5.relative(projectRoot, fullPath) || path5.basename(fullPath) : path5.relative(projectRoot, target) || path5.basename(target);
1915
+ await scheduleReload(displayPath);
1916
+ });
1917
+ }
1885
1918
  }
1886
1919
  const server = http.createServer(async (req, res) => {
1887
1920
  const method = (req.method || "GET").toUpperCase();