@solongate/proxy 0.1.2 → 0.1.4
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/create.js +263 -0
- package/dist/index.js +1213 -84
- package/dist/inject.js +339 -0
- package/package.json +3 -3
package/dist/create.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/create.ts
|
|
4
|
+
import { mkdirSync, writeFileSync, existsSync } from "fs";
|
|
5
|
+
import { resolve, join } from "path";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
function log(msg) {
|
|
8
|
+
process.stderr.write(msg + "\n");
|
|
9
|
+
}
|
|
10
|
+
function parseCreateArgs(argv) {
|
|
11
|
+
const args = argv.slice(2);
|
|
12
|
+
const opts = {
|
|
13
|
+
name: "",
|
|
14
|
+
policy: "restricted",
|
|
15
|
+
noInstall: false
|
|
16
|
+
};
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
switch (args[i]) {
|
|
19
|
+
case "--policy":
|
|
20
|
+
opts.policy = args[++i];
|
|
21
|
+
break;
|
|
22
|
+
case "--no-install":
|
|
23
|
+
opts.noInstall = true;
|
|
24
|
+
break;
|
|
25
|
+
case "--help":
|
|
26
|
+
case "-h":
|
|
27
|
+
printHelp();
|
|
28
|
+
process.exit(0);
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
if (!args[i].startsWith("-") && !opts.name) {
|
|
32
|
+
opts.name = args[i];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!opts.name) {
|
|
37
|
+
log("");
|
|
38
|
+
log(" Error: Project name required.");
|
|
39
|
+
log("");
|
|
40
|
+
log(" Usage: npx @solongate/proxy create <name>");
|
|
41
|
+
log("");
|
|
42
|
+
log(" Examples:");
|
|
43
|
+
log(" npx @solongate/proxy create my-mcp-server");
|
|
44
|
+
log(" npx @solongate/proxy create weather-api");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
return opts;
|
|
48
|
+
}
|
|
49
|
+
function printHelp() {
|
|
50
|
+
log(`
|
|
51
|
+
SolonGate Create \u2014 Scaffold a secure MCP server in seconds
|
|
52
|
+
|
|
53
|
+
USAGE
|
|
54
|
+
npx @solongate/proxy create <name> [options]
|
|
55
|
+
|
|
56
|
+
OPTIONS
|
|
57
|
+
--policy <preset> Policy preset (default: restricted)
|
|
58
|
+
--no-install Skip dependency installation
|
|
59
|
+
-h, --help Show this help message
|
|
60
|
+
|
|
61
|
+
EXAMPLES
|
|
62
|
+
npx @solongate/proxy create my-server
|
|
63
|
+
npx @solongate/proxy create db-tools --policy read-only
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
function createProject(dir, name, _policy) {
|
|
67
|
+
writeFileSync(
|
|
68
|
+
join(dir, "package.json"),
|
|
69
|
+
JSON.stringify(
|
|
70
|
+
{
|
|
71
|
+
name,
|
|
72
|
+
version: "0.1.0",
|
|
73
|
+
type: "module",
|
|
74
|
+
private: true,
|
|
75
|
+
bin: { [name]: "./dist/index.js" },
|
|
76
|
+
scripts: {
|
|
77
|
+
build: "tsup src/index.ts --format esm",
|
|
78
|
+
dev: "tsx src/index.ts",
|
|
79
|
+
start: "node dist/index.js"
|
|
80
|
+
},
|
|
81
|
+
dependencies: {
|
|
82
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
83
|
+
"@solongate/sdk": "latest",
|
|
84
|
+
zod: "^3.25.0"
|
|
85
|
+
},
|
|
86
|
+
devDependencies: {
|
|
87
|
+
tsup: "^8.3.0",
|
|
88
|
+
tsx: "^4.19.0",
|
|
89
|
+
typescript: "^5.7.0"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
null,
|
|
93
|
+
2
|
|
94
|
+
) + "\n"
|
|
95
|
+
);
|
|
96
|
+
writeFileSync(
|
|
97
|
+
join(dir, "tsconfig.json"),
|
|
98
|
+
JSON.stringify(
|
|
99
|
+
{
|
|
100
|
+
compilerOptions: {
|
|
101
|
+
target: "ES2022",
|
|
102
|
+
module: "ESNext",
|
|
103
|
+
moduleResolution: "bundler",
|
|
104
|
+
esModuleInterop: true,
|
|
105
|
+
strict: true,
|
|
106
|
+
outDir: "dist",
|
|
107
|
+
rootDir: "src",
|
|
108
|
+
declaration: true,
|
|
109
|
+
skipLibCheck: true
|
|
110
|
+
},
|
|
111
|
+
include: ["src"]
|
|
112
|
+
},
|
|
113
|
+
null,
|
|
114
|
+
2
|
|
115
|
+
) + "\n"
|
|
116
|
+
);
|
|
117
|
+
mkdirSync(join(dir, "src"), { recursive: true });
|
|
118
|
+
writeFileSync(
|
|
119
|
+
join(dir, "src", "index.ts"),
|
|
120
|
+
`#!/usr/bin/env node
|
|
121
|
+
|
|
122
|
+
// MCP uses stdout for JSON-RPC \u2014 redirect console to stderr
|
|
123
|
+
console.log = (...args: unknown[]) => {
|
|
124
|
+
process.stderr.write(args.map(String).join(' ') + '\\n');
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
import { SecureMcpServer } from '@solongate/sdk';
|
|
128
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
129
|
+
import { z } from 'zod';
|
|
130
|
+
|
|
131
|
+
// Create a secure MCP server (API key from SOLONGATE_API_KEY env var)
|
|
132
|
+
const server = new SecureMcpServer({
|
|
133
|
+
name: '${name}',
|
|
134
|
+
version: '0.1.0',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// \u2500\u2500 Register your tools below \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
138
|
+
|
|
139
|
+
server.tool(
|
|
140
|
+
'hello',
|
|
141
|
+
'Say hello to someone',
|
|
142
|
+
{ name: z.string().describe('Name of the person to greet') },
|
|
143
|
+
async ({ name }) => ({
|
|
144
|
+
content: [{ type: 'text', text: \`Hello, \${name}! Welcome to ${name}.\` }],
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Example: Add more tools here
|
|
149
|
+
// server.tool(
|
|
150
|
+
// 'read_data',
|
|
151
|
+
// 'Read data from a source',
|
|
152
|
+
// { query: z.string().describe('What to read') },
|
|
153
|
+
// async ({ query }) => ({
|
|
154
|
+
// content: [{ type: 'text', text: \`Result for: \${query}\` }],
|
|
155
|
+
// }),
|
|
156
|
+
// );
|
|
157
|
+
|
|
158
|
+
// \u2500\u2500 Start the server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
159
|
+
|
|
160
|
+
const transport = new StdioServerTransport();
|
|
161
|
+
await server.connect(transport);
|
|
162
|
+
console.log('${name} is running');
|
|
163
|
+
`
|
|
164
|
+
);
|
|
165
|
+
writeFileSync(
|
|
166
|
+
join(dir, ".mcp.json"),
|
|
167
|
+
JSON.stringify(
|
|
168
|
+
{
|
|
169
|
+
mcpServers: {
|
|
170
|
+
[name]: {
|
|
171
|
+
command: "node",
|
|
172
|
+
args: ["dist/index.js"],
|
|
173
|
+
env: {
|
|
174
|
+
SOLONGATE_API_KEY: "sg_test_e4460d32_replace_with_your_key"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
null,
|
|
180
|
+
2
|
|
181
|
+
) + "\n"
|
|
182
|
+
);
|
|
183
|
+
writeFileSync(
|
|
184
|
+
join(dir, ".gitignore"),
|
|
185
|
+
`node_modules/
|
|
186
|
+
dist/
|
|
187
|
+
*.solongate-backup
|
|
188
|
+
.env
|
|
189
|
+
.env.local
|
|
190
|
+
`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function withSpinner(message, fn) {
|
|
194
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
195
|
+
let i = 0;
|
|
196
|
+
const id = setInterval(() => {
|
|
197
|
+
process.stderr.write(`\r ${frames[i++ % frames.length]} ${message}`);
|
|
198
|
+
}, 80);
|
|
199
|
+
try {
|
|
200
|
+
fn();
|
|
201
|
+
clearInterval(id);
|
|
202
|
+
process.stderr.write(`\r \u2713 ${message}
|
|
203
|
+
`);
|
|
204
|
+
} catch {
|
|
205
|
+
clearInterval(id);
|
|
206
|
+
process.stderr.write(`\r \u2717 ${message} \u2014 failed
|
|
207
|
+
`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function installDeps(dir) {
|
|
211
|
+
withSpinner("Installing dependencies...", () => {
|
|
212
|
+
execSync("npm install", { cwd: dir, stdio: "pipe" });
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async function main() {
|
|
216
|
+
const opts = parseCreateArgs(process.argv);
|
|
217
|
+
const dir = resolve(opts.name);
|
|
218
|
+
log("");
|
|
219
|
+
log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
220
|
+
log(" \u2551 SolonGate \u2014 Create MCP Server \u2551");
|
|
221
|
+
log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
|
|
222
|
+
log("");
|
|
223
|
+
if (existsSync(dir)) {
|
|
224
|
+
log(` Error: Directory "${opts.name}" already exists.`);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
mkdirSync(dir, { recursive: true });
|
|
228
|
+
log(` Project: ${opts.name}`);
|
|
229
|
+
log(` Language: TypeScript`);
|
|
230
|
+
log(` Policy: ${opts.policy}`);
|
|
231
|
+
log("");
|
|
232
|
+
createProject(dir, opts.name, opts.policy);
|
|
233
|
+
log(" Files created:");
|
|
234
|
+
log(" package.json");
|
|
235
|
+
log(" tsconfig.json");
|
|
236
|
+
log(" src/index.ts");
|
|
237
|
+
log(" .mcp.json");
|
|
238
|
+
log(" .gitignore");
|
|
239
|
+
log("");
|
|
240
|
+
if (!opts.noInstall) {
|
|
241
|
+
installDeps(dir);
|
|
242
|
+
log("");
|
|
243
|
+
}
|
|
244
|
+
const W = 46;
|
|
245
|
+
const line = (s) => log(` \u2502 ${s.padEnd(W)} \u2502`);
|
|
246
|
+
log(` \u250C${"\u2500".repeat(W + 2)}\u2510`);
|
|
247
|
+
line("Project created!");
|
|
248
|
+
line("");
|
|
249
|
+
line(`cd ${opts.name}`);
|
|
250
|
+
line("");
|
|
251
|
+
line("npm run build # Build");
|
|
252
|
+
line("npm run dev # Dev mode (tsx)");
|
|
253
|
+
line("npm start # Run built server");
|
|
254
|
+
line("");
|
|
255
|
+
line("Set your API key:");
|
|
256
|
+
line("export SOLONGATE_API_KEY=sg_live_xxx");
|
|
257
|
+
log(` \u2514${"\u2500".repeat(W + 2)}\u2518`);
|
|
258
|
+
log("");
|
|
259
|
+
}
|
|
260
|
+
main().catch((err) => {
|
|
261
|
+
log(`Fatal: ${err instanceof Error ? err.message : String(err)}`);
|
|
262
|
+
process.exit(1);
|
|
263
|
+
});
|