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 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,3 @@
1
+ SETTLEGRID_TOOL_SLUG={{TOOL_SLUG}}
2
+ # Add your API keys here:
3
+ # API_KEY=your-key-here
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ *.tsbuildinfo
@@ -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,3 @@
1
+ SETTLEGRID_TOOL_SLUG={{TOOL_SLUG}}
2
+ # Add your API keys here:
3
+ # API_KEY=your-key-here
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ *.tsbuildinfo
@@ -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,5 @@
1
+ SETTLEGRID_TOOL_SLUG={{TOOL_SLUG}}
2
+ PORT=3000
3
+ # Your upstream API base URL:
4
+ # UPSTREAM_API_URL=https://api.example.com
5
+ # UPSTREAM_API_KEY=your-key-here
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ *.tsbuildinfo
@@ -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,4 @@
1
+ SETTLEGRID_TOOL_SLUG={{TOOL_SLUG}}
2
+ PORT=3000
3
+ # Add your API keys here:
4
+ # API_KEY=your-key-here
@@ -0,0 +1,4 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ *.tsbuildinfo
@@ -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
+ }