mcp-client-gen 0.0.2 → 0.0.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 +50 -0
- package/dist/index.js +103 -10
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -145,3 +145,53 @@ Examples:
|
|
|
145
145
|
- Authentication handling
|
|
146
146
|
- Streaming support
|
|
147
147
|
- Error handling and retries
|
|
148
|
+
|
|
149
|
+
## Authentication
|
|
150
|
+
|
|
151
|
+
Generated MCP clients include built-in support for OAuth 2.1 authentication using RFC 7591 Dynamic Client Registration. The authentication flow is handled automatically:
|
|
152
|
+
|
|
153
|
+
### OAuth 2.1 Support
|
|
154
|
+
|
|
155
|
+
- **Dynamic Client Registration (RFC 7591)** - Clients automatically register with OAuth providers
|
|
156
|
+
- **PKCE Flow (RFC 7636)** - Secure authorization code exchange with Proof Key for Code Exchange
|
|
157
|
+
- **Multiple Auth Methods** - Supports `client_secret_basic`, `client_secret_post`, and public clients
|
|
158
|
+
- **Token Management** - Automatic token refresh and credential storage
|
|
159
|
+
- **Resource Protection** - RFC 9728 OAuth 2.0 Protected Resource Metadata support
|
|
160
|
+
|
|
161
|
+
### Authentication Flow
|
|
162
|
+
|
|
163
|
+
1. **Discovery** - Client discovers OAuth authorization server metadata
|
|
164
|
+
2. **Registration** - Dynamic client registration if credentials not found
|
|
165
|
+
3. **Authorization** - PKCE-based authorization code flow initiation
|
|
166
|
+
4. **Token Exchange** - Secure token exchange with automatic refresh
|
|
167
|
+
5. **API Calls** - Authenticated requests using Bearer tokens
|
|
168
|
+
|
|
169
|
+
### Configuration
|
|
170
|
+
|
|
171
|
+
Authentication is configured per MCP server in your `.mcp.json`:
|
|
172
|
+
|
|
173
|
+
```jsonc
|
|
174
|
+
{
|
|
175
|
+
"mcpServers": {
|
|
176
|
+
"secured-service": {
|
|
177
|
+
"type": "http",
|
|
178
|
+
"url": "https://api.example.com/mcp",
|
|
179
|
+
"auth": {
|
|
180
|
+
"type": "oauth",
|
|
181
|
+
"clientId": "your-client-id", // Optional for dynamic registration
|
|
182
|
+
"scopes": ["read", "write"]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Support & License
|
|
190
|
+
|
|
191
|
+
If this tool helps you build amazing integrations, consider [sponsoring the project](https://github.com/sponsors/koistya) to support continued development. 💖
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
**MIT Licensed** • Feel free to use this in your commercial projects, contribute back, or fork it entirely. Code should be free! 🔓
|
|
196
|
+
|
|
197
|
+
Built with ❤️ by [Konstantin Tarkus](https://github.com/koistya) and [contributors](https://github.com/kriasoft/mcp-client-gen/graphs/contributors).
|
package/dist/index.js
CHANGED
|
@@ -152,8 +152,9 @@ import { resolve as resolve3 } from "node:path";
|
|
|
152
152
|
|
|
153
153
|
// node_modules/@clack/core/dist/index.mjs
|
|
154
154
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
155
|
-
import { stdin as j, stdout as M } from "node:process";
|
|
156
155
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
156
|
+
import { stdin as j, stdout as M } from "node:process";
|
|
157
|
+
import * as g from "node:readline";
|
|
157
158
|
import O from "node:readline";
|
|
158
159
|
import { Writable as X } from "node:stream";
|
|
159
160
|
function DD({ onlyFirst: e = false } = {}) {
|
|
@@ -386,6 +387,28 @@ function m(e, u) {
|
|
|
386
387
|
const t = e;
|
|
387
388
|
t.isTTY && t.setRawMode(u);
|
|
388
389
|
}
|
|
390
|
+
function fD({ input: e = j, output: u = M, overwrite: t = true, hideCursor: F = true } = {}) {
|
|
391
|
+
const s = g.createInterface({ input: e, output: u, prompt: "", tabSize: 1 });
|
|
392
|
+
g.emitKeypressEvents(e, s), e.isTTY && e.setRawMode(true);
|
|
393
|
+
const i = (D, { name: C, sequence: n }) => {
|
|
394
|
+
const E = String(D);
|
|
395
|
+
if ($([E, C, n], "cancel")) {
|
|
396
|
+
F && u.write(import_sisteransi.cursor.show), process.exit(0);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (!t)
|
|
400
|
+
return;
|
|
401
|
+
const a = C === "return" ? 0 : -1, o = C === "return" ? -1 : 0;
|
|
402
|
+
g.moveCursor(u, a, o, () => {
|
|
403
|
+
g.clearLine(u, 1, () => {
|
|
404
|
+
e.once("keypress", i);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
};
|
|
408
|
+
return F && u.write(import_sisteransi.cursor.hide), e.once("keypress", i), () => {
|
|
409
|
+
e.off("keypress", i), F && u.write(import_sisteransi.cursor.show), e.isTTY && !AD && e.setRawMode(false), s.terminal = false, s.close();
|
|
410
|
+
};
|
|
411
|
+
}
|
|
389
412
|
var gD = Object.defineProperty;
|
|
390
413
|
var vD = (e, u, t) => (u in e) ? gD(e, u, { enumerable: true, configurable: true, writable: true, value: t }) : e[u] = t;
|
|
391
414
|
var h = (e, u, t) => (vD(e, typeof u != "symbol" ? u + "" : u, t), t);
|
|
@@ -587,9 +610,9 @@ var G2 = (t) => {
|
|
|
587
610
|
const { cursor: n, options: r2, style: i } = t, s = t.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), a = Math.min(c, Math.max(s, 5));
|
|
588
611
|
let l2 = 0;
|
|
589
612
|
n >= l2 + a - 3 ? l2 = Math.max(Math.min(n - a + 3, r2.length - a), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
|
|
590
|
-
const $2 = a < r2.length && l2 > 0,
|
|
613
|
+
const $2 = a < r2.length && l2 > 0, g2 = a < r2.length && l2 + a < r2.length;
|
|
591
614
|
return r2.slice(l2, l2 + a).map((p2, v2, f) => {
|
|
592
|
-
const j2 = v2 === 0 && $2, E = v2 === f.length - 1 &&
|
|
615
|
+
const j2 = v2 === 0 && $2, E = v2 === f.length - 1 && g2;
|
|
593
616
|
return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v2 + l2 === n);
|
|
594
617
|
});
|
|
595
618
|
};
|
|
@@ -671,6 +694,57 @@ ${import_picocolors2.default.gray(d2)} ${t}
|
|
|
671
694
|
`);
|
|
672
695
|
};
|
|
673
696
|
var J = `${import_picocolors2.default.gray(o)} `;
|
|
697
|
+
var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
698
|
+
const n = V2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], r2 = V2 ? 80 : 120, i = process.env.CI === "true";
|
|
699
|
+
let s, c, a = false, l2 = "", $2, g2 = performance.now();
|
|
700
|
+
const p2 = (m2) => {
|
|
701
|
+
const h2 = m2 > 1 ? "Something went wrong" : "Canceled";
|
|
702
|
+
a && N2(h2, m2);
|
|
703
|
+
}, v2 = () => p2(2), f = () => p2(1), j2 = () => {
|
|
704
|
+
process.on("uncaughtExceptionMonitor", v2), process.on("unhandledRejection", v2), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
|
|
705
|
+
}, E = () => {
|
|
706
|
+
process.removeListener("uncaughtExceptionMonitor", v2), process.removeListener("unhandledRejection", v2), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
|
|
707
|
+
}, B2 = () => {
|
|
708
|
+
if ($2 === undefined)
|
|
709
|
+
return;
|
|
710
|
+
i && process.stdout.write(`
|
|
711
|
+
`);
|
|
712
|
+
const m2 = $2.split(`
|
|
713
|
+
`);
|
|
714
|
+
process.stdout.write(import_sisteransi2.cursor.move(-999, m2.length - 1)), process.stdout.write(import_sisteransi2.erase.down(m2.length));
|
|
715
|
+
}, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
|
|
716
|
+
const h2 = (performance.now() - m2) / 1000, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
|
|
717
|
+
return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
|
|
718
|
+
}, H2 = (m2 = "") => {
|
|
719
|
+
a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
|
|
720
|
+
`);
|
|
721
|
+
let h2 = 0, w2 = 0;
|
|
722
|
+
j2(), c = setInterval(() => {
|
|
723
|
+
if (i && l2 === $2)
|
|
724
|
+
return;
|
|
725
|
+
B2(), $2 = l2;
|
|
726
|
+
const I2 = import_picocolors2.default.magenta(n[h2]);
|
|
727
|
+
if (i)
|
|
728
|
+
process.stdout.write(`${I2} ${l2}...`);
|
|
729
|
+
else if (t === "timer")
|
|
730
|
+
process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
|
|
731
|
+
else {
|
|
732
|
+
const z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
|
|
733
|
+
process.stdout.write(`${I2} ${l2}${z2}`);
|
|
734
|
+
}
|
|
735
|
+
h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
|
|
736
|
+
}, r2);
|
|
737
|
+
}, N2 = (m2 = "", h2 = 0) => {
|
|
738
|
+
a = false, clearInterval(c), B2();
|
|
739
|
+
const w2 = h2 === 0 ? import_picocolors2.default.green(C) : h2 === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
|
|
740
|
+
l2 = R2(m2 ?? l2), t === "timer" ? process.stdout.write(`${w2} ${l2} ${O2(g2)}
|
|
741
|
+
`) : process.stdout.write(`${w2} ${l2}
|
|
742
|
+
`), E(), s();
|
|
743
|
+
};
|
|
744
|
+
return { start: H2, stop: N2, message: (m2 = "") => {
|
|
745
|
+
l2 = R2(m2 ?? l2);
|
|
746
|
+
} };
|
|
747
|
+
};
|
|
674
748
|
|
|
675
749
|
// lib/prompts.ts
|
|
676
750
|
import { existsSync as existsSync2 } from "node:fs";
|
|
@@ -830,7 +904,7 @@ async function runInteractiveSetup(cwd = process.cwd(), useDefaults = false) {
|
|
|
830
904
|
const configFiles = await promptForConfigFiles(cwd);
|
|
831
905
|
const servers = await promptForServers(configFiles);
|
|
832
906
|
const outputFile = await promptForOutputFile(cwd);
|
|
833
|
-
Se(`\
|
|
907
|
+
Se(`\uD83C\uDF89 Configuration complete! Generating client for ${servers.length} server${servers.length !== 1 ? "s" : ""}`);
|
|
834
908
|
return {
|
|
835
909
|
configFiles,
|
|
836
910
|
servers,
|
|
@@ -841,6 +915,28 @@ async function runInteractiveSetup(cwd = process.cwd(), useDefaults = false) {
|
|
|
841
915
|
process.exit(1);
|
|
842
916
|
}
|
|
843
917
|
}
|
|
918
|
+
async function introspectServers(servers) {
|
|
919
|
+
const s = Y2();
|
|
920
|
+
s.start(`Introspecting ${servers.length} MCP server${servers.length !== 1 ? "s" : ""}...`);
|
|
921
|
+
const results = [];
|
|
922
|
+
for (const [index, server] of servers.entries()) {
|
|
923
|
+
s.message(`[${index + 1}/${servers.length}] Connecting to ${server.url}...`);
|
|
924
|
+
await new Promise((resolve3) => setTimeout(resolve3, 800));
|
|
925
|
+
s.message(`[${index + 1}/${servers.length}] Fetching capabilities from ${server.url}...`);
|
|
926
|
+
await new Promise((resolve3) => setTimeout(resolve3, 600));
|
|
927
|
+
results.push({
|
|
928
|
+
server,
|
|
929
|
+
tools: Math.floor(Math.random() * 10) + 5,
|
|
930
|
+
resources: Math.floor(Math.random() * 5),
|
|
931
|
+
prompts: Math.floor(Math.random() * 3)
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
const totalTools = results.reduce((sum, r2) => sum + r2.tools, 0);
|
|
935
|
+
const totalResources = results.reduce((sum, r2) => sum + r2.resources, 0);
|
|
936
|
+
const totalPrompts = results.reduce((sum, r2) => sum + r2.prompts, 0);
|
|
937
|
+
s.stop(`✅ Successfully introspected ${servers.length} server${servers.length !== 1 ? "s" : ""}: ${totalTools} tools, ${totalResources} resources, ${totalPrompts} prompts`);
|
|
938
|
+
return results;
|
|
939
|
+
}
|
|
844
940
|
|
|
845
941
|
// index.ts
|
|
846
942
|
function showHelp() {
|
|
@@ -956,11 +1052,7 @@ Usage:`);
|
|
|
956
1052
|
console.log(`const result = await ${serverNames[0]}.fetchPage("123");`);
|
|
957
1053
|
}
|
|
958
1054
|
async function generateMCPClientFromServers(servers, outputFile) {
|
|
959
|
-
|
|
960
|
-
console.log(`Servers: ${servers.map((s) => s.url).join(", ")}`);
|
|
961
|
-
console.log("⏳ Connecting to MCP servers...");
|
|
962
|
-
console.log("⏳ Fetching server capabilities...");
|
|
963
|
-
console.log("⏳ Generating TypeScript client...");
|
|
1055
|
+
const introspectionResults = await introspectServers(servers);
|
|
964
1056
|
const serverNames = servers.map((_3, index) => `server${index + 1}`);
|
|
965
1057
|
const clientExports = servers.map((server, index) => `export const server${index + 1} = new Server${index + 1}Client("${server.url}");`).join(`
|
|
966
1058
|
`);
|
|
@@ -982,7 +1074,8 @@ ${clientClasses}
|
|
|
982
1074
|
|
|
983
1075
|
${clientExports}
|
|
984
1076
|
`;
|
|
985
|
-
console.log(
|
|
1077
|
+
console.log(`
|
|
1078
|
+
✅ Generated client saved to ${outputFile}`);
|
|
986
1079
|
console.log(`
|
|
987
1080
|
Usage:`);
|
|
988
1081
|
console.log(`import { ${serverNames[0]} } from "${outputFile.replace(".ts", ".js")}";`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-client-gen",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Interactive CLI tool that generates type-safe TypeScript clients from MCP servers",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
],
|
|
13
13
|
"keywords": [
|
|
14
14
|
"mcp",
|
|
15
|
+
"mcp-client",
|
|
15
16
|
"client",
|
|
16
17
|
"generator",
|
|
17
18
|
"sdk",
|
|
@@ -24,10 +25,17 @@
|
|
|
24
25
|
"prompts",
|
|
25
26
|
"schema",
|
|
26
27
|
"tool",
|
|
27
|
-
"type-safe"
|
|
28
|
+
"type-safe",
|
|
29
|
+
"model-context-protocol",
|
|
30
|
+
"oauth",
|
|
31
|
+
"oauth2",
|
|
32
|
+
"authentication",
|
|
33
|
+
"pkce",
|
|
34
|
+
"rfc7591"
|
|
28
35
|
],
|
|
29
36
|
"author": "Konstantin Tarkus <koistya@kriasoft.com>",
|
|
30
37
|
"license": "MIT",
|
|
38
|
+
"funding": "https://github.com/sponsors/koistya",
|
|
31
39
|
"repository": {
|
|
32
40
|
"type": "git",
|
|
33
41
|
"url": "https://github.com/kriasoft/mcp-client-gen.git"
|