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 +81 -85
- package/dist/cli/index.js +42 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,50 +3,37 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/opentool)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
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
|
-
##
|
|
10
|
+
## What is it?
|
|
11
11
|
|
|
12
|
-
OpenTool
|
|
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
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
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
|
-
##
|
|
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
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
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
|
-
|
|
27
|
+
## Features
|
|
46
28
|
|
|
47
|
-
- **
|
|
48
|
-
- **
|
|
49
|
-
- **
|
|
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
|
|
92
|
+
### 4. Enable MCP mode (optional)
|
|
106
93
|
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
248
|
+
Push your repo to GitHub and connect it to [OpenPond](https://openpond.ai):
|
|
248
249
|
|
|
249
|
-
1.
|
|
250
|
-
2.
|
|
251
|
-
3.
|
|
252
|
-
4.
|
|
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
|
-
|
|
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
|
|
266
|
+
# Test the example
|
|
271
267
|
cd examples/full-metadata
|
|
272
268
|
npm link opentool
|
|
273
269
|
npm run build
|
|
274
270
|
|
|
275
|
-
#
|
|
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
|
-
#
|
|
282
|
-
npm run examples:build # Build
|
|
283
|
-
npm run examples:validate # Validate
|
|
284
|
-
npm run examples:metadata # Regenerate metadata.json
|
|
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
|
|
285
|
+
OpenTool has three levels of metadata config:
|
|
290
286
|
|
|
291
|
-
1. **
|
|
292
|
-
2. **
|
|
293
|
-
3. **
|
|
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
|
|
291
|
+
See [`METADATA.md`](./METADATA.md) for details on configuring metadata for on-chain registration and payments.
|
|
296
292
|
|
|
297
|
-
##
|
|
293
|
+
## What's Next
|
|
298
294
|
|
|
299
|
-
-
|
|
295
|
+
- Better watch mode that keeps metadata and tool artifacts synced during dev
|
|
300
296
|
|
|
301
297
|
## Contributing
|
|
302
298
|
|
|
303
|
-
|
|
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
|
-
|
|
1870
|
-
|
|
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 ${
|
|
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();
|