create-settlegrid-tool 1.0.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/dist/index.js +295 -0
- package/package.json +65 -0
- package/templates/blank/README.md +35 -0
- package/templates/blank/_dot_env.example +3 -0
- package/templates/blank/_dot_gitignore +4 -0
- package/templates/blank/package.json +18 -0
- package/templates/blank/src/server.ts +24 -0
- package/templates/blank/tsconfig.json +16 -0
- package/templates/mcp-server/README.md +55 -0
- package/templates/mcp-server/_dot_env.example +3 -0
- package/templates/mcp-server/_dot_gitignore +4 -0
- package/templates/mcp-server/package.json +20 -0
- package/templates/mcp-server/src/server.ts +61 -0
- package/templates/mcp-server/tsconfig.json +16 -0
- package/templates/openapi/README.md +53 -0
- package/templates/openapi/_dot_env.example +5 -0
- package/templates/openapi/_dot_gitignore +4 -0
- package/templates/openapi/package.json +20 -0
- package/templates/openapi/src/server.ts +79 -0
- package/templates/openapi/tsconfig.json +16 -0
- package/templates/rest-api/README.md +40 -0
- package/templates/rest-api/_dot_env.example +4 -0
- package/templates/rest-api/_dot_gitignore +4 -0
- package/templates/rest-api/package.json +20 -0
- package/templates/rest-api/src/server.ts +46 -0
- package/templates/rest-api/tsconfig.json +16 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
import pc3 from "picocolors";
|
|
6
|
+
import path2 from "path";
|
|
7
|
+
|
|
8
|
+
// src/scaffold.ts
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import pc from "picocolors";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
var __dirname = path.dirname(__filename);
|
|
15
|
+
function getTemplatesDir() {
|
|
16
|
+
const candidates = [
|
|
17
|
+
path.resolve(__dirname, "..", "templates"),
|
|
18
|
+
path.resolve(__dirname, "..", "..", "templates")
|
|
19
|
+
];
|
|
20
|
+
for (const candidate of candidates) {
|
|
21
|
+
if (fs.existsSync(candidate)) {
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return candidates[0];
|
|
26
|
+
}
|
|
27
|
+
function replacePlaceholders(content, config) {
|
|
28
|
+
return content.replace(/\{\{TOOL_NAME\}\}/g, config.toolName).replace(/\{\{TOOL_SLUG\}\}/g, config.toolSlug).replace(/\{\{DESCRIPTION\}\}/g, config.description).replace(/\{\{PRICE_CENTS\}\}/g, String(config.priceCents)).replace(/\{\{CATEGORY\}\}/g, config.category).replace(/\{\{PRICING_MODEL\}\}/g, config.pricingModel);
|
|
29
|
+
}
|
|
30
|
+
async function copyTemplateDir(templateDir, targetDir, config) {
|
|
31
|
+
const entries = await fs.readdir(templateDir, { withFileTypes: true });
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
const srcPath = path.join(templateDir, entry.name);
|
|
34
|
+
const targetName = entry.name.startsWith("_dot_") ? "." + entry.name.slice(5) : entry.name;
|
|
35
|
+
const destPath = path.join(targetDir, targetName);
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
await fs.ensureDir(destPath);
|
|
38
|
+
await copyTemplateDir(srcPath, destPath, config);
|
|
39
|
+
} else {
|
|
40
|
+
const content = await fs.readFile(srcPath, "utf-8");
|
|
41
|
+
const processed = replacePlaceholders(content, config);
|
|
42
|
+
await fs.writeFile(destPath, processed, "utf-8");
|
|
43
|
+
console.log(` ${pc.green("+")} ${path.relative(config.targetDir, destPath)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function scaffold(config) {
|
|
48
|
+
const templatesDir = getTemplatesDir();
|
|
49
|
+
if (await fs.pathExists(config.targetDir)) {
|
|
50
|
+
const files = await fs.readdir(config.targetDir);
|
|
51
|
+
if (files.length > 0) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Directory "${config.directory}" already exists and is not empty.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
await fs.ensureDir(config.targetDir);
|
|
58
|
+
const templatePath = path.join(templatesDir, config.template);
|
|
59
|
+
if (!await fs.pathExists(templatePath)) {
|
|
60
|
+
throw new Error(`Template "${config.template}" not found at ${templatePath}`);
|
|
61
|
+
}
|
|
62
|
+
await copyTemplateDir(templatePath, config.targetDir, config);
|
|
63
|
+
if (config.deployTarget === "vercel") {
|
|
64
|
+
const vercelConfig = JSON.stringify(
|
|
65
|
+
{
|
|
66
|
+
buildCommand: "npm run build",
|
|
67
|
+
outputDirectory: "dist"
|
|
68
|
+
},
|
|
69
|
+
null,
|
|
70
|
+
2
|
|
71
|
+
);
|
|
72
|
+
await fs.writeFile(
|
|
73
|
+
path.join(config.targetDir, "vercel.json"),
|
|
74
|
+
vercelConfig + "\n",
|
|
75
|
+
"utf-8"
|
|
76
|
+
);
|
|
77
|
+
console.log(` ${pc.green("+")} vercel.json`);
|
|
78
|
+
}
|
|
79
|
+
if (config.deployTarget === "docker") {
|
|
80
|
+
const dockerfile = `FROM node:20-slim AS builder
|
|
81
|
+
WORKDIR /app
|
|
82
|
+
COPY package*.json ./
|
|
83
|
+
RUN npm ci
|
|
84
|
+
COPY . .
|
|
85
|
+
RUN npm run build
|
|
86
|
+
|
|
87
|
+
FROM node:20-slim
|
|
88
|
+
WORKDIR /app
|
|
89
|
+
COPY --from=builder /app/dist ./dist
|
|
90
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
91
|
+
COPY --from=builder /app/package.json ./
|
|
92
|
+
EXPOSE 3000
|
|
93
|
+
CMD ["node", "dist/server.js"]
|
|
94
|
+
`;
|
|
95
|
+
await fs.writeFile(
|
|
96
|
+
path.join(config.targetDir, "Dockerfile"),
|
|
97
|
+
dockerfile,
|
|
98
|
+
"utf-8"
|
|
99
|
+
);
|
|
100
|
+
console.log(` ${pc.green("+")} Dockerfile`);
|
|
101
|
+
const dockerignore = `node_modules
|
|
102
|
+
dist
|
|
103
|
+
.env
|
|
104
|
+
.git
|
|
105
|
+
`;
|
|
106
|
+
await fs.writeFile(
|
|
107
|
+
path.join(config.targetDir, ".dockerignore"),
|
|
108
|
+
dockerignore,
|
|
109
|
+
"utf-8"
|
|
110
|
+
);
|
|
111
|
+
console.log(` ${pc.green("+")} .dockerignore`);
|
|
112
|
+
}
|
|
113
|
+
if (config.deployTarget === "railway") {
|
|
114
|
+
const railwayToml = `[build]
|
|
115
|
+
builder = "nixpacks"
|
|
116
|
+
|
|
117
|
+
[deploy]
|
|
118
|
+
startCommand = "npm start"
|
|
119
|
+
`;
|
|
120
|
+
await fs.writeFile(
|
|
121
|
+
path.join(config.targetDir, "railway.toml"),
|
|
122
|
+
railwayToml,
|
|
123
|
+
"utf-8"
|
|
124
|
+
);
|
|
125
|
+
console.log(` ${pc.green("+")} railway.toml`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/banner.ts
|
|
130
|
+
import pc2 from "picocolors";
|
|
131
|
+
function banner() {
|
|
132
|
+
const emerald = pc2.green;
|
|
133
|
+
const dim = pc2.dim;
|
|
134
|
+
const art = `
|
|
135
|
+
${emerald(" ___ _ _ _ ___ _ _ ")}
|
|
136
|
+
${emerald(" / __| ___| |_| |_| |___ / __|_ __(_)__| |")}
|
|
137
|
+
${emerald(" \\__ \\/ -_) _| _| / -_)| (_ | '_| / _` |")}
|
|
138
|
+
${emerald(" |___/\\___|\\__|\\__|_\\___| \\___|_| |_\\__,_|")}
|
|
139
|
+
`;
|
|
140
|
+
const tagline = dim(" The Settlement Layer for the AI Economy");
|
|
141
|
+
const version = dim(" v1.0.0");
|
|
142
|
+
const separator = dim(" " + "-".repeat(44));
|
|
143
|
+
return `${art}${tagline}
|
|
144
|
+
${version}
|
|
145
|
+
${separator}
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/index.ts
|
|
150
|
+
var CATEGORIES = [
|
|
151
|
+
{ title: "Data", value: "data" },
|
|
152
|
+
{ title: "NLP", value: "nlp" },
|
|
153
|
+
{ title: "Search", value: "search" },
|
|
154
|
+
{ title: "Finance", value: "finance" },
|
|
155
|
+
{ title: "Code", value: "code" },
|
|
156
|
+
{ title: "Security", value: "security" },
|
|
157
|
+
{ title: "Analytics", value: "analytics" },
|
|
158
|
+
{ title: "Other", value: "other" }
|
|
159
|
+
];
|
|
160
|
+
var PRICING_MODELS = [
|
|
161
|
+
{ title: "Per call", value: "per-call" },
|
|
162
|
+
{ title: "Per token", value: "per-token" },
|
|
163
|
+
{ title: "Per byte", value: "per-byte" }
|
|
164
|
+
];
|
|
165
|
+
var TEMPLATES = [
|
|
166
|
+
{ title: "Blank \u2014 minimal settlegrid.init() + sg.wrap()", value: "blank" },
|
|
167
|
+
{ title: "REST API \u2014 Express.js server with billing middleware", value: "rest-api" },
|
|
168
|
+
{ title: "OpenAPI \u2014 scaffold from an OpenAPI spec", value: "openapi" },
|
|
169
|
+
{ title: "MCP Server \u2014 full MCP server with stdio transport", value: "mcp-server" }
|
|
170
|
+
];
|
|
171
|
+
var DEPLOY_TARGETS = [
|
|
172
|
+
{ title: "Vercel", value: "vercel" },
|
|
173
|
+
{ title: "Railway", value: "railway" },
|
|
174
|
+
{ title: "Docker", value: "docker" },
|
|
175
|
+
{ title: "None", value: "none" }
|
|
176
|
+
];
|
|
177
|
+
function toSlug(name) {
|
|
178
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
179
|
+
}
|
|
180
|
+
async function main() {
|
|
181
|
+
console.log(banner());
|
|
182
|
+
const dirArg = process.argv[2];
|
|
183
|
+
if (dirArg === "--help" || dirArg === "-h") {
|
|
184
|
+
console.log(`
|
|
185
|
+
${pc3.bold("Usage:")} npx create-settlegrid-tool ${pc3.cyan("[directory]")}
|
|
186
|
+
|
|
187
|
+
${pc3.dim("Creates a monetized AI tool project with SettleGrid billing.")}
|
|
188
|
+
|
|
189
|
+
${pc3.bold("Options:")}
|
|
190
|
+
${pc3.cyan("[directory]")} Project directory name (default: prompted)
|
|
191
|
+
${pc3.cyan("--help, -h")} Show this help message
|
|
192
|
+
${pc3.cyan("--version")} Show version
|
|
193
|
+
`);
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
if (dirArg === "--version") {
|
|
197
|
+
console.log("1.0.0");
|
|
198
|
+
process.exit(0);
|
|
199
|
+
}
|
|
200
|
+
let cancelled = false;
|
|
201
|
+
const response = await prompts(
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
type: dirArg ? null : "text",
|
|
205
|
+
name: "directory",
|
|
206
|
+
message: "Project directory",
|
|
207
|
+
initial: "my-tool",
|
|
208
|
+
validate: (v) => v.length > 0 ? true : "Directory name is required"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
type: "text",
|
|
212
|
+
name: "toolName",
|
|
213
|
+
message: "Tool name",
|
|
214
|
+
initial: (prev) => prev || dirArg || "my-tool"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: "text",
|
|
218
|
+
name: "description",
|
|
219
|
+
message: "Description",
|
|
220
|
+
initial: "A monetized AI tool powered by SettleGrid"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: "select",
|
|
224
|
+
name: "category",
|
|
225
|
+
message: "Category",
|
|
226
|
+
choices: [...CATEGORIES]
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: "select",
|
|
230
|
+
name: "pricingModel",
|
|
231
|
+
message: "Pricing model",
|
|
232
|
+
choices: [...PRICING_MODELS]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
type: "number",
|
|
236
|
+
name: "priceCents",
|
|
237
|
+
message: "Default price per call (cents)",
|
|
238
|
+
initial: 2,
|
|
239
|
+
min: 0
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
type: "select",
|
|
243
|
+
name: "template",
|
|
244
|
+
message: "Template",
|
|
245
|
+
choices: [...TEMPLATES]
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: "select",
|
|
249
|
+
name: "deployTarget",
|
|
250
|
+
message: "Deploy target",
|
|
251
|
+
choices: [...DEPLOY_TARGETS]
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
{
|
|
255
|
+
onCancel: () => {
|
|
256
|
+
cancelled = true;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
if (cancelled) {
|
|
261
|
+
console.log(pc3.red("\nSetup cancelled."));
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
const directory = dirArg || response.directory;
|
|
265
|
+
const toolSlug = toSlug(directory);
|
|
266
|
+
const targetDir = path2.resolve(process.cwd(), directory);
|
|
267
|
+
const config = {
|
|
268
|
+
directory,
|
|
269
|
+
toolName: response.toolName || directory,
|
|
270
|
+
toolSlug,
|
|
271
|
+
description: response.description || "A monetized AI tool powered by SettleGrid",
|
|
272
|
+
category: response.category || "other",
|
|
273
|
+
pricingModel: response.pricingModel || "per-call",
|
|
274
|
+
priceCents: response.priceCents ?? 2,
|
|
275
|
+
template: response.template || "blank",
|
|
276
|
+
deployTarget: response.deployTarget || "none",
|
|
277
|
+
targetDir
|
|
278
|
+
};
|
|
279
|
+
console.log();
|
|
280
|
+
console.log(pc3.dim(" Scaffolding project..."));
|
|
281
|
+
console.log();
|
|
282
|
+
await scaffold(config);
|
|
283
|
+
console.log(pc3.green(pc3.bold(" Done!")) + " Your tool is ready.\n");
|
|
284
|
+
console.log(` ${pc3.dim("$")} ${pc3.cyan(`cd ${directory}`)}`);
|
|
285
|
+
console.log(` ${pc3.dim("$")} ${pc3.cyan("npm install")}`);
|
|
286
|
+
console.log(` ${pc3.dim("$")} ${pc3.cyan("npm run dev")}
|
|
287
|
+
`);
|
|
288
|
+
console.log(
|
|
289
|
+
pc3.dim(" Next: Register your tool at ") + pc3.cyan(pc3.underline("https://settlegrid.ai/dashboard/tools")) + "\n"
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
main().catch((err) => {
|
|
293
|
+
console.error(pc3.red("Error:"), err instanceof Error ? err.message : err);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-settlegrid-tool",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a monetized AI tool with SettleGrid in 30 seconds",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"settlegrid",
|
|
7
|
+
"mcp",
|
|
8
|
+
"ai",
|
|
9
|
+
"monetization",
|
|
10
|
+
"cli",
|
|
11
|
+
"scaffold",
|
|
12
|
+
"create",
|
|
13
|
+
"tool",
|
|
14
|
+
"ai-economy",
|
|
15
|
+
"billing"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://settlegrid.ai",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/lexwhiting/settlegrid.git",
|
|
21
|
+
"directory": "packages/create-settlegrid-tool"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/lexwhiting/settlegrid/issues",
|
|
25
|
+
"email": "support@settlegrid.ai"
|
|
26
|
+
},
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "Alerterra, LLC",
|
|
29
|
+
"email": "support@settlegrid.ai",
|
|
30
|
+
"url": "https://settlegrid.ai"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"bin": {
|
|
35
|
+
"create-settlegrid-tool": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"templates"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18.0.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"lint": "tsc --noEmit",
|
|
50
|
+
"prepublishOnly": "npm run build"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"fs-extra": "^11.2.0",
|
|
54
|
+
"picocolors": "^1.1.0",
|
|
55
|
+
"prompts": "^2.4.2"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/fs-extra": "^11.0.4",
|
|
59
|
+
"@types/node": "^22.0.0",
|
|
60
|
+
"@types/prompts": "^2.4.9",
|
|
61
|
+
"tsup": "^8.3.0",
|
|
62
|
+
"typescript": "^5.7.0",
|
|
63
|
+
"vitest": "^2.1.0"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# {{TOOL_NAME}}
|
|
2
|
+
|
|
3
|
+
{{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm run dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Pricing
|
|
13
|
+
|
|
14
|
+
| Method | Cost |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `query` | {{PRICE_CENTS}} cents per call |
|
|
17
|
+
|
|
18
|
+
Billing is handled automatically by [SettleGrid](https://settlegrid.ai). Users purchase credits and are billed per call.
|
|
19
|
+
|
|
20
|
+
## Development
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm run dev # Start with hot reload
|
|
24
|
+
npm run build # Compile TypeScript
|
|
25
|
+
npm start # Run compiled output
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Deploy
|
|
29
|
+
|
|
30
|
+
Register your tool at [settlegrid.ai/dashboard/tools](https://settlegrid.ai/dashboard/tools) to start accepting payments.
|
|
31
|
+
|
|
32
|
+
## Documentation
|
|
33
|
+
|
|
34
|
+
- [SettleGrid Docs](https://settlegrid.ai/docs)
|
|
35
|
+
- [@settlegrid/mcp SDK](https://www.npmjs.com/package/@settlegrid/mcp)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{TOOL_SLUG}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{DESCRIPTION}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx src/server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/server.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@settlegrid/mcp": "^0.1.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsx": "^4.0.0",
|
|
16
|
+
"typescript": "^5.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { settlegrid } from '@settlegrid/mcp'
|
|
2
|
+
|
|
3
|
+
const sg = settlegrid.init({
|
|
4
|
+
toolSlug: '{{TOOL_SLUG}}', // Replace with your registered slug from settlegrid.ai
|
|
5
|
+
pricing: {
|
|
6
|
+
defaultCostCents: {{PRICE_CENTS}},
|
|
7
|
+
methods: {
|
|
8
|
+
'query': { costCents: {{PRICE_CENTS}}, displayName: 'Query' },
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
// Your tool handler — replace with your actual logic
|
|
14
|
+
async function handleQuery(args: { query: string }) {
|
|
15
|
+
// TODO: Implement your tool logic here
|
|
16
|
+
return { result: `Response for: ${args.query}` }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Wrap with SettleGrid billing
|
|
20
|
+
export const query = sg.wrap(handleQuery, { method: 'query' })
|
|
21
|
+
|
|
22
|
+
// Example usage:
|
|
23
|
+
// const result = await query({ query: 'hello world' })
|
|
24
|
+
// console.log(result)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# {{TOOL_NAME}}
|
|
2
|
+
|
|
3
|
+
{{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
A full MCP (Model Context Protocol) server with SettleGrid billing.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## MCP Configuration
|
|
15
|
+
|
|
16
|
+
Add to your MCP client configuration (e.g., Claude Desktop):
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"mcpServers": {
|
|
21
|
+
"{{TOOL_SLUG}}": {
|
|
22
|
+
"command": "node",
|
|
23
|
+
"args": ["dist/server.js"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Tools
|
|
30
|
+
|
|
31
|
+
| Tool | Description | Cost |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `query` | Run a query | {{PRICE_CENTS}} cents per call |
|
|
34
|
+
|
|
35
|
+
## Pricing
|
|
36
|
+
|
|
37
|
+
Billing is handled automatically by [SettleGrid](https://settlegrid.ai). Users purchase credits and are billed per call.
|
|
38
|
+
|
|
39
|
+
## Development
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm run dev # Start with hot reload
|
|
43
|
+
npm run build # Compile TypeScript
|
|
44
|
+
npm start # Run compiled output
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Deploy
|
|
48
|
+
|
|
49
|
+
Register your tool at [settlegrid.ai/dashboard/tools](https://settlegrid.ai/dashboard/tools) to start accepting payments.
|
|
50
|
+
|
|
51
|
+
## Documentation
|
|
52
|
+
|
|
53
|
+
- [MCP Specification](https://modelcontextprotocol.io)
|
|
54
|
+
- [SettleGrid Docs](https://settlegrid.ai/docs)
|
|
55
|
+
- [@settlegrid/mcp SDK](https://www.npmjs.com/package/@settlegrid/mcp)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{TOOL_SLUG}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{DESCRIPTION}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx src/server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/server.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
13
|
+
"@settlegrid/mcp": "^0.1.1",
|
|
14
|
+
"zod": "^3.23.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"tsx": "^4.0.0",
|
|
18
|
+
"typescript": "^5.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
import { settlegrid } from '@settlegrid/mcp'
|
|
5
|
+
|
|
6
|
+
// Initialize SettleGrid billing
|
|
7
|
+
const sg = settlegrid.init({
|
|
8
|
+
toolSlug: '{{TOOL_SLUG}}', // Replace with your registered slug from settlegrid.ai
|
|
9
|
+
pricing: {
|
|
10
|
+
defaultCostCents: {{PRICE_CENTS}},
|
|
11
|
+
methods: {
|
|
12
|
+
'query': { costCents: {{PRICE_CENTS}}, displayName: 'Query' },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Wrap your tool handler with SettleGrid billing
|
|
18
|
+
const query = sg.wrap(
|
|
19
|
+
async (args: { query: string }) => {
|
|
20
|
+
// TODO: Implement your tool logic here
|
|
21
|
+
return { result: `Response for: ${args.query}` }
|
|
22
|
+
},
|
|
23
|
+
{ method: 'query' }
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
// Create the MCP server
|
|
27
|
+
const server = new McpServer({
|
|
28
|
+
name: '{{TOOL_SLUG}}',
|
|
29
|
+
version: '1.0.0',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Register tools
|
|
33
|
+
server.tool(
|
|
34
|
+
'query',
|
|
35
|
+
'Run a query against {{TOOL_NAME}}',
|
|
36
|
+
{
|
|
37
|
+
query: z.string().describe('The query to run'),
|
|
38
|
+
},
|
|
39
|
+
async ({ query: queryText }) => {
|
|
40
|
+
const result = await query({ query: queryText })
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'text' as const,
|
|
45
|
+
text: JSON.stringify(result, null, 2),
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
// Start the server with stdio transport
|
|
53
|
+
async function main() {
|
|
54
|
+
const transport = new StdioServerTransport()
|
|
55
|
+
await server.connect(transport)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
main().catch((error) => {
|
|
59
|
+
console.error('Server error:', error)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# {{TOOL_NAME}}
|
|
2
|
+
|
|
3
|
+
{{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
Built from an OpenAPI spec with [SettleGrid](https://settlegrid.ai) billing.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The server starts at `http://localhost:3000`.
|
|
15
|
+
|
|
16
|
+
## Adding Endpoints
|
|
17
|
+
|
|
18
|
+
Add endpoints from your OpenAPI spec in `src/server.ts`. Each endpoint is wrapped with SettleGrid billing:
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
const myEndpoint = sg.wrap(
|
|
22
|
+
async (args: { id: string }) => {
|
|
23
|
+
const response = await fetch(`${UPSTREAM_URL}/resource/${args.id}`)
|
|
24
|
+
return response.json()
|
|
25
|
+
},
|
|
26
|
+
{ method: 'get-resource' }
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Pricing
|
|
31
|
+
|
|
32
|
+
| Method | Cost |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| Default | {{PRICE_CENTS}} cents per call |
|
|
35
|
+
|
|
36
|
+
Billing is handled automatically by [SettleGrid](https://settlegrid.ai).
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm run dev # Start with hot reload
|
|
42
|
+
npm run build # Compile TypeScript
|
|
43
|
+
npm start # Run compiled output
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Deploy
|
|
47
|
+
|
|
48
|
+
Register your tool at [settlegrid.ai/dashboard/tools](https://settlegrid.ai/dashboard/tools) to start accepting payments.
|
|
49
|
+
|
|
50
|
+
## Documentation
|
|
51
|
+
|
|
52
|
+
- [SettleGrid Docs](https://settlegrid.ai/docs)
|
|
53
|
+
- [@settlegrid/mcp SDK](https://www.npmjs.com/package/@settlegrid/mcp)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{TOOL_SLUG}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{DESCRIPTION}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/server.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@settlegrid/mcp": "^0.1.1",
|
|
13
|
+
"express": "^4.21.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/express": "^5.0.0",
|
|
17
|
+
"tsx": "^4.0.0",
|
|
18
|
+
"typescript": "^5.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { settlegrid } from '@settlegrid/mcp'
|
|
3
|
+
|
|
4
|
+
const app = express()
|
|
5
|
+
app.use(express.json())
|
|
6
|
+
|
|
7
|
+
const UPSTREAM_URL = process.env.UPSTREAM_API_URL || 'https://api.example.com'
|
|
8
|
+
|
|
9
|
+
const sg = settlegrid.init({
|
|
10
|
+
toolSlug: '{{TOOL_SLUG}}', // Replace with your registered slug from settlegrid.ai
|
|
11
|
+
pricing: {
|
|
12
|
+
defaultCostCents: {{PRICE_CENTS}},
|
|
13
|
+
methods: {
|
|
14
|
+
// Add a method entry for each OpenAPI endpoint you want to monetize:
|
|
15
|
+
// 'get-resource': { costCents: {{PRICE_CENTS}}, displayName: 'Get Resource' },
|
|
16
|
+
// 'search': { costCents: {{PRICE_CENTS}}, displayName: 'Search' },
|
|
17
|
+
// 'create-resource': { costCents: 5, displayName: 'Create Resource' },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// ------------------------------------------------------------------
|
|
23
|
+
// Add your OpenAPI endpoints below.
|
|
24
|
+
//
|
|
25
|
+
// For each endpoint in your OpenAPI spec, create a wrapped handler:
|
|
26
|
+
//
|
|
27
|
+
// const getResource = sg.wrap(
|
|
28
|
+
// async (args: { id: string }) => {
|
|
29
|
+
// const res = await fetch(`${UPSTREAM_URL}/resources/${args.id}`, {
|
|
30
|
+
// headers: { 'Authorization': `Bearer ${process.env.UPSTREAM_API_KEY}` },
|
|
31
|
+
// })
|
|
32
|
+
// if (!res.ok) throw new Error(`Upstream error: ${res.status}`)
|
|
33
|
+
// return res.json()
|
|
34
|
+
// },
|
|
35
|
+
// { method: 'get-resource' }
|
|
36
|
+
// )
|
|
37
|
+
//
|
|
38
|
+
// app.get('/resources/:id', async (req, res) => {
|
|
39
|
+
// try {
|
|
40
|
+
// const result = await getResource({ id: req.params.id })
|
|
41
|
+
// res.json(result)
|
|
42
|
+
// } catch (error) {
|
|
43
|
+
// const message = error instanceof Error ? error.message : 'Internal server error'
|
|
44
|
+
// res.status(500).json({ error: message })
|
|
45
|
+
// }
|
|
46
|
+
// })
|
|
47
|
+
// ------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
// Example endpoint — replace with your actual OpenAPI operations
|
|
50
|
+
const query = sg.wrap(
|
|
51
|
+
async (args: { query: string }) => {
|
|
52
|
+
// TODO: Forward to your upstream API
|
|
53
|
+
// const res = await fetch(`${UPSTREAM_URL}/search?q=${encodeURIComponent(args.query)}`)
|
|
54
|
+
// return res.json()
|
|
55
|
+
return { result: `Response for: ${args.query}`, upstream: UPSTREAM_URL }
|
|
56
|
+
},
|
|
57
|
+
{ method: 'query' }
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
app.post('/query', async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const result = await query(req.body)
|
|
63
|
+
res.json(result)
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : 'Internal server error'
|
|
66
|
+
res.status(500).json({ error: message })
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Health check (no billing)
|
|
71
|
+
app.get('/health', (_req, res) => {
|
|
72
|
+
res.json({ status: 'ok', tool: '{{TOOL_SLUG}}', timestamp: new Date().toISOString() })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const PORT = process.env.PORT || 3000
|
|
76
|
+
app.listen(PORT, () => {
|
|
77
|
+
console.log(`{{TOOL_NAME}} running at http://localhost:${PORT}`)
|
|
78
|
+
console.log(`Health check: http://localhost:${PORT}/health`)
|
|
79
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# {{TOOL_NAME}}
|
|
2
|
+
|
|
3
|
+
{{DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm run dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The server starts at `http://localhost:3000`.
|
|
13
|
+
|
|
14
|
+
## Endpoints
|
|
15
|
+
|
|
16
|
+
| Endpoint | Method | Cost |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `POST /query` | query | {{PRICE_CENTS}} cents per call |
|
|
19
|
+
| `GET /health` | - | Free |
|
|
20
|
+
|
|
21
|
+
## Pricing
|
|
22
|
+
|
|
23
|
+
Billing is handled automatically by [SettleGrid](https://settlegrid.ai). Users purchase credits and are billed per call via the `x-settlegrid-api-key` header.
|
|
24
|
+
|
|
25
|
+
## Development
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm run dev # Start with hot reload (tsx watch)
|
|
29
|
+
npm run build # Compile TypeScript
|
|
30
|
+
npm start # Run compiled output
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Deploy
|
|
34
|
+
|
|
35
|
+
Register your tool at [settlegrid.ai/dashboard/tools](https://settlegrid.ai/dashboard/tools) to start accepting payments.
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
38
|
+
|
|
39
|
+
- [SettleGrid Docs](https://settlegrid.ai/docs)
|
|
40
|
+
- [@settlegrid/mcp SDK](https://www.npmjs.com/package/@settlegrid/mcp)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{TOOL_SLUG}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{DESCRIPTION}}",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/server.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@settlegrid/mcp": "^0.1.1",
|
|
13
|
+
"express": "^4.21.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/express": "^5.0.0",
|
|
17
|
+
"tsx": "^4.0.0",
|
|
18
|
+
"typescript": "^5.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { settlegrid } from '@settlegrid/mcp'
|
|
3
|
+
|
|
4
|
+
const app = express()
|
|
5
|
+
app.use(express.json())
|
|
6
|
+
|
|
7
|
+
const sg = settlegrid.init({
|
|
8
|
+
toolSlug: '{{TOOL_SLUG}}', // Replace with your registered slug from settlegrid.ai
|
|
9
|
+
pricing: {
|
|
10
|
+
defaultCostCents: {{PRICE_CENTS}},
|
|
11
|
+
methods: {
|
|
12
|
+
'query': { costCents: {{PRICE_CENTS}}, displayName: 'Query' },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Wrap your handler with SettleGrid billing
|
|
18
|
+
const query = sg.wrap(
|
|
19
|
+
async (args: { query: string }) => {
|
|
20
|
+
// TODO: Implement your tool logic here
|
|
21
|
+
return { result: `Response for: ${args.query}` }
|
|
22
|
+
},
|
|
23
|
+
{ method: 'query' }
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
// Routes
|
|
27
|
+
app.post('/query', async (req, res) => {
|
|
28
|
+
try {
|
|
29
|
+
const result = await query(req.body)
|
|
30
|
+
res.json(result)
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : 'Internal server error'
|
|
33
|
+
res.status(500).json({ error: message })
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Health check (no billing)
|
|
38
|
+
app.get('/health', (_req, res) => {
|
|
39
|
+
res.json({ status: 'ok', tool: '{{TOOL_SLUG}}', timestamp: new Date().toISOString() })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const PORT = process.env.PORT || 3000
|
|
43
|
+
app.listen(PORT, () => {
|
|
44
|
+
console.log(`{{TOOL_NAME}} running at http://localhost:${PORT}`)
|
|
45
|
+
console.log(`Health check: http://localhost:${PORT}/health`)
|
|
46
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|