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.
Files changed (3) hide show
  1. package/README.md +50 -0
  2. package/dist/index.js +103 -10
  3. 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, g = a < r2.length && l2 + a < r2.length;
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 && g;
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(`\x05 Configuration complete! Generating client for ${servers.length} server${servers.length !== 1 ? "s" : ""}`);
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
- console.log(`Generating MCP client SDK from ${servers.length} servers...`);
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(`✅ Generated client saved to ${outputFile}`);
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.2",
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"