opentool
v0.8.28
Published
OpenTool framework for building serverless MCP tools
Maintainers
Readme
OpenTool
Build serverless TypeScript tools that work with AI assistants, handle crypto payments, and deploy to AWS Lambda automatically.
For LLMs/AI Code Generation: context/opentool-context.ts
What is it?
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:
Tools are either Public or Private. Public tools are accessible to the public and are monetized with crypto payments using x402. Private tools are accessible only to the app developer and are mainly for trading and onchain interaction use cases.
Installation
npm install opentoolQuick Start
1. Create a new project
mkdir my-opentool-project
cd my-opentool-project
npm install opentool
npx opentool init2. Create your first tool
Create a tools/ directory and add your first tool:
// tools/greet.ts
import { z } from "zod";
export const schema = z.object({
name: z.string().describe("The name of the user to greet"),
});
export const metadata = {
name: "greet",
description: "Simple greeting tool",
};
export async function POST(request: Request) {
const payload = await request.json();
const { name } = schema.parse(payload);
return Response.json({
message: `Hello, ${name}!`,
});
}3. Test locally
# Validate your tools
npx opentool validate
# Start development server
npx opentool devPrivate tools: GET-only and POST-only
For private tools, say for internal trading apps:
- GET-only (scheduled default profile)
- POST-only (one-off, parameterized with Zod)
profile.categorydefaults totrackerif omitted; set tostrategyororchestratorfor PnL/automation tools.- Strategy tools must define
profile.templatePreviewwith: subtitle(required short line)description(required multi-line summary, 3-8 non-empty lines; target ~5)titleis optional and defaults to the tool name when omitted.
GET-only (scheduled default)
// tools/aave-stake.ts
export const profile = {
description: "Stake 100 USDC daily at 12:00 UTC",
category: "strategy",
schedule: { cron: "0 12 * * *", enabled: false },
templatePreview: {
subtitle: "Automated daily staking strategy",
description: [
"Runs once per day on your configured schedule.",
"Uses fixed, explicit sizing controls from template config.",
"Designed for long-running automated execution.",
"Keeps logic deterministic and easy to audit.",
"Best for hands-off recurring onchain actions.",
].join("\\n"),
},
};
export async function GET(_req: Request) {
return Response.json({
ok: true,
action: "stake",
});
}POST-only (one-off)
// tools/aave-unstake.ts
import { z } from "zod";
export const profile = {
description: "Unstake USDC on demand",
category: "tracker",
notifyEmail: true,
};
export const schema = z.object({
amount: z.string(),
token: z.string().default("USDC"),
});
export async function POST(req: Request) {
const body = await req.json();
const { amount, token } = schema.parse(body);
return Response.json({ ok: true, action: "unstake", amount, token });
}Email notifications for one-off tools
- POST-only tools can set
profile.notifyEmail = trueto request an email when the tool runs. - Scheduled tools should continue to use
profile.schedule.notifyEmail.
Cron schedules (profile.schedule)
- GET-only tools require
profile.schedulewith a standard 5–6 field cron expression (e.g.,0 12 * * *or0 0 ? * MON-FRI *). - Build validates the cron shape and emits
dist/tools.jsonwith schedule data per tool. Enabled defaults tofalseeven if authors set it totruein code. Deployment targets can translate these cron strings to their provider format (e.g., EventBridge) downstream. - Use
profile.schedule.notifyEmail = trueto request email delivery on schedule runs.
Public tools: Add x402 payments (optional)
Protect your public tools with crypto payments using x402:
// tools/premium-report.ts
import { z } from "zod";
import { defineX402Payment } from "opentool/x402";
export const schema = z.object({
symbol: z.string().describe("Crypto symbol (e.g., BTC)"),
});
export const payment = defineX402Payment({
amount: "0.001",
payTo: process.env.WALLET_ADDRESS!,
currency: "USDC",
network: "base-sepolia",
message: "Premium analytics require payment",
});
export async function POST(request: Request) {
const payload = await request.json();
const { symbol } = schema.parse(payload);
return Response.json({
report: `Premium analytics for ${symbol}`,
});
}Test the payment flow:
# Start dev server
WALLET_ADDRESS=0x... npx opentool dev --input tools
# Test with the x402 client
PRIVATE_KEY=0x... bun examples/full-metadata/test-x402.tsOr test manually:
# Get 402 response with payment requirements
curl -X POST http://localhost:7000/premium-report \
-H "content-type: application/json" \
-d '{"symbol":"BTC"}'
# Pay and retry with X-PAYMENT header (generated by x402 client)
curl -X POST http://localhost:7000/premium-report \
-H "content-type: application/json" \
-H "X-PAYMENT: ${X402_HEADER}" \
-d '{"symbol":"BTC"}'MCP
By default, tools are HTTP-only. Want them accessible via MCP clients like Claude Desktop? Just add this to your tool file:
// tools/greet.ts
export const mcp = {
enabled: true, // Now works with Claude Desktop, MCP Inspector, etc.
};Tools without this export stay HTTP-only, which is useful when you want selective access. Mix and match as needed.
Testing with MCP Inspector
The examples/full-metadata project has an inspector.json config ready to go:
cd examples/full-metadata
npx mcp-inspector --config inspector.json --server opentool-devCopy .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.
Quick x402 test with curl
Start the dev server against the example tools:
npx opentool dev --input examples/full-metadata/toolsTrigger the paywall and inspect the returned payment requirements:
curl -i \ -X POST http://localhost:7000/premium-report \ -H "content-type: application/json" \ -d '{"symbol":"BTC"}'The response includes a
402 Payment Requiredstatus and JSON body with anx402.accepts[0]object describing the payment request.Submit a follow-up request with an
X-PAYMENTheader produced by your x402 facilitator (for example, by using the Coinbase x402 tooling or your own signing flow):curl -i \ -X POST http://localhost:7000/premium-report \ -H "content-type: application/json" \ -H "X-PAYMENT: ${X402_HEADER}" \ -d '{"symbol":"BTC"}'Replace
${X402_HEADER}with the base64-encoded payment payload returned by your facilitator’s/verifyor/payworkflow. If the payment is valid the server responds with200 OK; otherwise it returns a new402with failure details.
5. Build for deployment
# Build tools for Lambda deployment
npx opentool build6. Deploy to OpenPond
Create an account on OpenPond and create a new project.
Add your project to the OpenPond project and connect it to your GitHub repository.
OpenPond will automatically detect the opentool dependency and deploy your tools to AWS Lambda.
CLI Commands
Build
Build your tools for deployment:
npx opentool build [options]
Options:
-i, --input <dir> Input directory containing tools (default: "tools")
-o, --output <dir> Output directory for built tools (default: "dist")
--name <name> Server name (default: "opentool-server")
--version <version> Server version (default: "1.0.0")Development Server
Start a local development server:
npx opentool dev [options]
Options:
-i, --input <dir> Input directory containing tools (default: "tools")
--watch Watch for file changes (default: false)Validate
Validate your tools:
npx opentool validate [options]
Options:
-i, --input <dir> Input directory containing tools (default: "tools")Generate Metadata
Generate metadata.json without building:
npx opentool metadata [options]
Options:
-i, --input <dir> Input directory containing tools (default: "tools")
-o, --output <file> Output file path for metadata.json (default: "metadata.json")
--name <name> Server name (default: "opentool-server")
--version <version> Server version (default: "1.0.0")Generates the metadata file with tool schemas, payment configs, and discovery info. Useful for inspecting or sharing metadata without a full build.
Tool Definition
Tools are just TypeScript files with a few exports:
import { z } from "zod";
// 1. Schema for input validation
export const schema = z.object({
input: z.string().describe("Some input parameter"),
});
// 2. Metadata
export const metadata = {
name: "my_tool",
description: "What this tool does",
};
// 3. Optional: enable MCP mode
export const mcp = {
enabled: true, // Makes it work with Claude Desktop, etc.
};
// 4. Handler (POST, GET, PUT, DELETE, etc.)
export async function POST(request: Request) {
const payload = await request.json();
const params = schema.parse(payload);
// Your tool logic here
return Response.json({
result: "Tool response",
});
}Error Handling
Just return standard HTTP responses:
export async function POST(request: Request) {
const payload = await request.json();
const params = schema.parse(payload);
if (someCondition) {
return Response.json({ error: "Something went wrong" }, { status: 400 });
}
return Response.json({ result: "Success" });
}Local Development
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:
- Testing tool logic
- Validating schemas
- Debugging before deployment
Deployment
Push your repo to GitHub and connect it to OpenPond:
- OpenPond detects the
opentooldependency - Runs
npx opentool build - Deploys to AWS Lambda with Function URLs
- Done - your tools are live
Examples
Check examples/full-metadata/ for a complete example with payment and discovery features.
Testing Examples Locally
# Build and link the OpenTool package
npm run build
npm link
# Test the example
cd examples/full-metadata
npm link opentool
npm run build
# Check the output
cat dist/metadata.json
# Test the MCP server
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | node dist/mcp-server.js
# Or from repo root:
npm run examples:build # Build example (CJS+ESM)
npm run examples:validate # Validate metadata and tools
npm run examples:metadata # Regenerate metadata.jsonMetadata System
OpenTool has three levels of metadata config:
- Default - pulls from your
package.jsonautomatically - Project-level (optional) - add a
metadata.tsfile for branding, payments, etc. - Tool-level - override metadata per tool
See METADATA.md for details on configuring metadata for on-chain registration and payments.
What's Next
- Better watch mode that keeps metadata and tool artifacts synced during dev
Contributing
Contributions welcome! See the Contributing Guide.
License
MIT © OpenTool
