mcp-playbook 0.1.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.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ // CommonJS entry point — works without type:module
5
+ // Delegates to the compiled CLI
6
+
7
+ const path = require('path')
8
+ const fs = require('fs')
9
+
10
+ const distCli = path.join(__dirname, '../dist/cli/index.js')
11
+
12
+ if (!fs.existsSync(distCli)) {
13
+ console.error('\n mcp-playbook: dist/ not found.')
14
+ console.error(' The package was not built before publishing.')
15
+ console.error(' If you are developing locally, run: npm run build\n')
16
+ process.exit(1)
17
+ }
18
+
19
+ // Use require for CJS build output from tsup
20
+ require(distCli).run().catch(err => {
21
+ console.error('Failed to start mcp-playbook:', err.message)
22
+ process.exit(1)
23
+ })
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ // Runs automatically after npm install
5
+ // Prints next steps so the developer knows what to do
6
+
7
+ const isInsideNodeModules = __dirname.includes('node_modules')
8
+ if (isInsideNodeModules) process.exit(0)
9
+
10
+ console.log('\n ⚡ mcp-playbook installed successfully')
11
+ console.log('\n Get started:')
12
+ console.log(' npx mcp-playbook init — create playbook.config.ts')
13
+ console.log(' npx mcp-playbook dev — start the dev server')
14
+ console.log(' npx mcp-playbook build — build static docs site\n')
@@ -0,0 +1,7 @@
1
+ /**
2
+ * src/cli/index.ts
3
+ * CLI entry point — dev, build, init commands
4
+ */
5
+ declare function run(): Promise<void>;
6
+
7
+ export { run };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * src/cli/index.ts
3
+ * CLI entry point — dev, build, init commands
4
+ */
5
+ declare function run(): Promise<void>;
6
+
7
+ export { run };
@@ -0,0 +1,513 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/server/connector.ts
34
+ async function discoverAllServers(servers, extraExamples = {}, tags = {}) {
35
+ const toolTagMap = {};
36
+ for (const [tag, tools] of Object.entries(tags)) {
37
+ for (const tool of tools) {
38
+ if (!toolTagMap[tool]) toolTagMap[tool] = [];
39
+ toolTagMap[tool].push(tag);
40
+ }
41
+ }
42
+ const results = await Promise.allSettled(
43
+ servers.map(
44
+ (server, i) => connectToServer(server, i, extraExamples, toolTagMap)
45
+ )
46
+ );
47
+ return results.map((result, i) => {
48
+ if (result.status === "fulfilled") {
49
+ return result.value;
50
+ } else {
51
+ return {
52
+ id: `server-${i}`,
53
+ name: servers[i].name,
54
+ url: servers[i].url || `${servers[i].command} ${(servers[i].args || []).join(" ")}`,
55
+ status: "error",
56
+ version: "unknown",
57
+ tools: [],
58
+ error: result.reason?.message || "Connection failed"
59
+ };
60
+ }
61
+ });
62
+ }
63
+ async function connectToServer(config, index, extraExamples, toolTagMap) {
64
+ const client = new import_client.Client(
65
+ { name: "mcp-playbook", version: "0.1.0" },
66
+ { capabilities: {} }
67
+ );
68
+ let transport;
69
+ if (config.transport === "stdio") {
70
+ if (!config.command) throw new Error(`Server "${config.name}" needs a command for stdio transport`);
71
+ transport = new import_stdio.StdioClientTransport({
72
+ command: config.command,
73
+ args: config.args || [],
74
+ env: config.env || {},
75
+ cwd: config.cwd || process.cwd()
76
+ });
77
+ } else if (config.transport === "http") {
78
+ if (!config.url) throw new Error(`Server "${config.name}" needs a url for http transport`);
79
+ transport = new import_streamableHttp.StreamableHTTPClientTransport(new URL(config.url), {
80
+ requestInit: { headers: config.headers || {} }
81
+ });
82
+ } else if (config.transport === "sse") {
83
+ if (!config.url) throw new Error(`Server "${config.name}" needs a url for sse transport`);
84
+ transport = new import_sse.SSEClientTransport(new URL(config.url), {
85
+ requestInit: { headers: config.headers || {} }
86
+ });
87
+ } else {
88
+ throw new Error(`Unknown transport: ${config.transport}`);
89
+ }
90
+ await client.connect(transport);
91
+ const { tools } = await client.listTools();
92
+ const serverInfo = client.getServerVersion();
93
+ const discoveredTools = tools.map((tool) => ({
94
+ name: tool.name,
95
+ description: tool.description || "",
96
+ inputSchema: tool.inputSchema,
97
+ examples: extraExamples[tool.name] || [],
98
+ tags: toolTagMap[tool.name] || []
99
+ }));
100
+ return {
101
+ id: `server-${index}`,
102
+ name: config.name,
103
+ url: config.url || `${config.command} ${(config.args || []).join(" ")}`,
104
+ status: "connected",
105
+ version: serverInfo?.version || "1.0.0",
106
+ tools: discoveredTools
107
+ };
108
+ }
109
+ async function executeTool(serverConfig, toolName, input) {
110
+ const start = Date.now();
111
+ try {
112
+ const client = new import_client.Client(
113
+ { name: "mcp-playbook", version: "0.1.0" },
114
+ { capabilities: {} }
115
+ );
116
+ let transport;
117
+ if (serverConfig.transport === "stdio") {
118
+ transport = new import_stdio.StdioClientTransport({
119
+ command: serverConfig.command,
120
+ args: serverConfig.args || []
121
+ });
122
+ } else {
123
+ transport = new import_streamableHttp.StreamableHTTPClientTransport(new URL(serverConfig.url));
124
+ }
125
+ await client.connect(transport);
126
+ const result = await client.callTool({ name: toolName, arguments: input });
127
+ await client.close();
128
+ return {
129
+ result,
130
+ duration_ms: Date.now() - start
131
+ };
132
+ } catch (err) {
133
+ return {
134
+ result: null,
135
+ duration_ms: Date.now() - start,
136
+ error: err.message
137
+ };
138
+ }
139
+ }
140
+ var import_client, import_stdio, import_sse, import_streamableHttp;
141
+ var init_connector = __esm({
142
+ "src/server/connector.ts"() {
143
+ "use strict";
144
+ import_client = require("@modelcontextprotocol/sdk/client/index.js");
145
+ import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
146
+ import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
147
+ import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
148
+ }
149
+ });
150
+
151
+ // src/server/dev-server.ts
152
+ var dev_server_exports = {};
153
+ __export(dev_server_exports, {
154
+ startDevServer: () => startDevServer
155
+ });
156
+ function resolveClientDist() {
157
+ const fromCompiled = import_path.default.join(__dirname, "../client");
158
+ if (import_fs.default.existsSync(fromCompiled)) return fromCompiled;
159
+ const fromRoot = import_path.default.join(__dirname, "../../dist/client");
160
+ if (import_fs.default.existsSync(fromRoot)) return fromRoot;
161
+ return fromCompiled;
162
+ }
163
+ function fallbackHTML(port) {
164
+ return `<!DOCTYPE html>
165
+ <html>
166
+ <head>
167
+ <meta charset="UTF-8"/>
168
+ <title>MCP Playbook</title>
169
+ <style>
170
+ body { font-family: monospace; background: #080808; color: #e2e0d8;
171
+ display: flex; align-items: center; justify-content: center;
172
+ height: 100vh; margin: 0; flex-direction: column; gap: 12px; }
173
+ code { background: #1a1a2e; color: #a39cf0; padding: 4px 10px;
174
+ border-radius: 4px; font-size: 14px; }
175
+ p { color: #5a5856; font-size: 13px; }
176
+ </style>
177
+ </head>
178
+ <body>
179
+ <div style="font-size:32px">\u26A1</div>
180
+ <h2 style="margin:0">MCP Playbook API is running</h2>
181
+ <p>The React UI bundle was not found at <code>dist/client/</code></p>
182
+ <p>Run <code>npm run build:client</code> inside the mcp-playbook library to build it.</p>
183
+ <p>API available at <code>http://localhost:${port}/api/servers</code></p>
184
+ </body>
185
+ </html>`;
186
+ }
187
+ async function startDevServer(options) {
188
+ const app = (0, import_express.default)();
189
+ const server = (0, import_http.createServer)(app);
190
+ const wss = new import_ws.WebSocketServer({ server });
191
+ app.use(import_express.default.json());
192
+ const clientDist = resolveClientDist();
193
+ const hasClient = import_fs.default.existsSync(import_path.default.join(clientDist, "index.html"));
194
+ if (hasClient) {
195
+ app.use(import_express.default.static(clientDist));
196
+ console.log(import_chalk.default.dim(` Client: serving from ${clientDist}`));
197
+ } else {
198
+ console.log(import_chalk.default.yellow(` Warning: client bundle not found at ${clientDist}`));
199
+ console.log(import_chalk.default.dim(" API is available but UI will show a placeholder"));
200
+ }
201
+ async function loadConfig() {
202
+ const key = require.resolve(options.configPath);
203
+ delete require.cache[key];
204
+ try {
205
+ return require(options.configPath).default;
206
+ } catch {
207
+ const jsPath = options.configPath.replace(/\.ts$/, ".js");
208
+ if (import_fs.default.existsSync(jsPath)) {
209
+ delete require.cache[require.resolve(jsPath)];
210
+ return require(jsPath).default;
211
+ }
212
+ throw new Error(
213
+ `Cannot load config: ${options.configPath}
214
+ Make sure ts-node is installed: npm install -D ts-node
215
+ Or compile your config to JS first.`
216
+ );
217
+ }
218
+ }
219
+ let cachedServers = [];
220
+ let cachedConfig = null;
221
+ async function refreshServers() {
222
+ try {
223
+ const config = await loadConfig();
224
+ cachedConfig = config;
225
+ cachedServers = await discoverAllServers(
226
+ config.servers,
227
+ config.examples || {},
228
+ config.tags || {}
229
+ );
230
+ const connected = cachedServers.filter((s) => s.status === "connected").length;
231
+ const tools = cachedServers.reduce((acc, s) => acc + s.tools.length, 0);
232
+ console.log(import_chalk.default.green(` \u2713 ${connected}/${cachedServers.length} servers \xB7 ${tools} tools`));
233
+ broadcastToClients({ type: "reload" });
234
+ } catch (err) {
235
+ console.error(import_chalk.default.red(` \u2717 ${err.message}`));
236
+ }
237
+ }
238
+ function broadcastToClients(data) {
239
+ const msg = JSON.stringify(data);
240
+ wss.clients.forEach((c) => {
241
+ if (c.readyState === 1) c.send(msg);
242
+ });
243
+ }
244
+ app.get("/api/servers", async (_req, res) => {
245
+ try {
246
+ res.json({
247
+ title: cachedConfig?.title || "MCP Playbook",
248
+ description: cachedConfig?.description || "",
249
+ theme: cachedConfig?.theme || {},
250
+ servers: cachedServers
251
+ });
252
+ } catch (err) {
253
+ res.status(500).json({ error: err.message });
254
+ }
255
+ });
256
+ app.post("/api/refresh", async (_req, res) => {
257
+ await refreshServers();
258
+ res.json({ ok: true });
259
+ });
260
+ app.post("/api/execute", async (req, res) => {
261
+ const { serverId, toolName, input } = req.body;
262
+ if (!cachedConfig) return res.status(503).json({ error: "Config not loaded" });
263
+ const idx = parseInt(serverId.replace("server-", ""));
264
+ const serverConfig = cachedConfig.servers[idx];
265
+ if (!serverConfig) return res.status(404).json({ error: `Server not found: ${serverId}` });
266
+ const result = await executeTool(serverConfig, toolName, input || {});
267
+ res.json(result);
268
+ });
269
+ app.get("*", (_req, res) => {
270
+ const indexPath = import_path.default.join(clientDist, "index.html");
271
+ if (import_fs.default.existsSync(indexPath)) {
272
+ res.sendFile(indexPath);
273
+ } else {
274
+ res.send(fallbackHTML(options.port));
275
+ }
276
+ });
277
+ wss.on("connection", (ws) => {
278
+ ws.send(JSON.stringify({ type: "connected" }));
279
+ });
280
+ const watcher = import_chokidar.default.watch(options.configPath, { ignoreInitial: true });
281
+ watcher.on("change", () => {
282
+ console.log(import_chalk.default.dim("\n Config changed \u2014 refreshing..."));
283
+ refreshServers();
284
+ });
285
+ await new Promise((resolve, reject) => {
286
+ server.on("error", (err) => {
287
+ if (err.code === "EADDRINUSE") {
288
+ reject(new Error(`Port ${options.port} is already in use. Try: --port ${options.port + 1}`));
289
+ } else {
290
+ reject(err);
291
+ }
292
+ });
293
+ server.listen(options.port, () => resolve());
294
+ });
295
+ await refreshServers();
296
+ const url = `http://localhost:${options.port}`;
297
+ console.log(import_chalk.default.dim(`
298
+ Local: `) + import_chalk.default.cyan(url));
299
+ console.log(import_chalk.default.dim(` Config: ${options.configPath}
300
+ `));
301
+ if (options.open && hasClient) {
302
+ try {
303
+ await (0, import_open.default)(url);
304
+ } catch {
305
+ }
306
+ }
307
+ return server;
308
+ }
309
+ var import_express, import_http, import_ws, import_path, import_fs, import_chalk, import_chokidar, import_open;
310
+ var init_dev_server = __esm({
311
+ "src/server/dev-server.ts"() {
312
+ "use strict";
313
+ import_express = __toESM(require("express"));
314
+ import_http = require("http");
315
+ import_ws = require("ws");
316
+ import_path = __toESM(require("path"));
317
+ import_fs = __toESM(require("fs"));
318
+ import_chalk = __toESM(require("chalk"));
319
+ import_chokidar = __toESM(require("chokidar"));
320
+ import_open = __toESM(require("open"));
321
+ init_connector();
322
+ }
323
+ });
324
+
325
+ // src/server/build.ts
326
+ var build_exports = {};
327
+ __export(build_exports, {
328
+ buildStaticSite: () => buildStaticSite
329
+ });
330
+ async function buildStaticSite(options) {
331
+ const configModule = await import(options.configPath);
332
+ const config = configModule.default;
333
+ const servers = await discoverAllServers(
334
+ config.servers,
335
+ config.examples || {},
336
+ config.tags || {}
337
+ );
338
+ import_fs2.default.mkdirSync(options.outputDir, { recursive: true });
339
+ const data = {
340
+ title: config.title || "MCP Playbook",
341
+ description: config.description || "",
342
+ theme: config.theme || {},
343
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
344
+ servers
345
+ };
346
+ import_fs2.default.writeFileSync(
347
+ import_path2.default.join(options.outputDir, "data.json"),
348
+ JSON.stringify(data, null, 2)
349
+ );
350
+ const clientDist = import_path2.default.join(
351
+ import_path2.default.dirname(new URL(import_meta.url).pathname),
352
+ "../../dist/client"
353
+ );
354
+ if (import_fs2.default.existsSync(clientDist)) {
355
+ copyDir(clientDist, options.outputDir);
356
+ }
357
+ const toolCount = servers.reduce((acc, s) => acc + s.tools.length, 0);
358
+ console.log(` ${servers.length} servers, ${toolCount} tools documented`);
359
+ }
360
+ function copyDir(src, dest) {
361
+ import_fs2.default.mkdirSync(dest, { recursive: true });
362
+ for (const entry of import_fs2.default.readdirSync(src, { withFileTypes: true })) {
363
+ const srcPath = import_path2.default.join(src, entry.name);
364
+ const destPath = import_path2.default.join(dest, entry.name);
365
+ if (entry.isDirectory()) {
366
+ copyDir(srcPath, destPath);
367
+ } else {
368
+ import_fs2.default.copyFileSync(srcPath, destPath);
369
+ }
370
+ }
371
+ }
372
+ var import_fs2, import_path2, import_meta;
373
+ var init_build = __esm({
374
+ "src/server/build.ts"() {
375
+ "use strict";
376
+ import_fs2 = __toESM(require("fs"));
377
+ import_path2 = __toESM(require("path"));
378
+ init_connector();
379
+ import_meta = {};
380
+ }
381
+ });
382
+
383
+ // src/cli/index.ts
384
+ var cli_exports = {};
385
+ __export(cli_exports, {
386
+ run: () => run
387
+ });
388
+ module.exports = __toCommonJS(cli_exports);
389
+ var import_commander = require("commander");
390
+ var import_chalk2 = __toESM(require("chalk"));
391
+ var import_path3 = __toESM(require("path"));
392
+ var import_fs3 = __toESM(require("fs"));
393
+ var pkgPath = import_path3.default.join(__dirname, "../../package.json");
394
+ var pkg = JSON.parse(import_fs3.default.readFileSync(pkgPath, "utf8"));
395
+ async function run() {
396
+ const program = new import_commander.Command();
397
+ program.name("mcp-playbook").description("Playbook for MCP tools \u2014 interactive docs and playground").version(pkg.version);
398
+ program.command("init").description("Create a playbook.config.ts in the current directory").action(() => {
399
+ const configPath = import_path3.default.resolve(process.cwd(), "playbook.config.ts");
400
+ if (import_fs3.default.existsSync(configPath)) {
401
+ console.log(import_chalk2.default.yellow("\n playbook.config.ts already exists\n"));
402
+ return;
403
+ }
404
+ const template = `import { defineConfig } from 'mcp-playbook'
405
+
406
+ export default defineConfig({
407
+ title: 'My MCP Playbook',
408
+ description: 'Interactive docs for our MCP tools',
409
+
410
+ servers: [
411
+ {
412
+ name: 'My API',
413
+ transport: 'stdio',
414
+ command: 'node',
415
+ args: ['./my-mcp-server.js'],
416
+ },
417
+
418
+ // HTTP transport example:
419
+ // {
420
+ // name: 'Remote API',
421
+ // transport: 'http',
422
+ // url: 'http://localhost:3001/mcp',
423
+ // },
424
+ ],
425
+
426
+ // Optional: hand-written examples per tool
427
+ examples: {
428
+ // get_user: [
429
+ // {
430
+ // label: 'Basic lookup',
431
+ // input: { userId: 'usr_abc123' }
432
+ // }
433
+ // ]
434
+ },
435
+
436
+ // Optional: group tools into sidebar sections
437
+ tags: {
438
+ // 'User management': ['get_user', 'create_user'],
439
+ },
440
+
441
+ theme: {
442
+ primary: '#7F77DD'
443
+ },
444
+
445
+ port: 4242
446
+ })
447
+ `;
448
+ import_fs3.default.writeFileSync(configPath, template, "utf8");
449
+ console.log(import_chalk2.default.green("\n \u2713 Created playbook.config.ts"));
450
+ console.log(import_chalk2.default.dim("\n Next steps:"));
451
+ console.log(import_chalk2.default.dim(" 1. Edit playbook.config.ts \u2014 point servers at your MCP server"));
452
+ console.log(import_chalk2.default.dim(" 2. Run: npx mcp-playbook dev"));
453
+ console.log(import_chalk2.default.dim(" 3. Open: http://localhost:4242\n"));
454
+ });
455
+ program.command("dev").description("Start dev server with hot reload").option("-p, --port <port>", "Port to run on", "4242").option("-c, --config <path>", "Path to config file", "playbook.config.ts").option("--no-open", "Do not open browser automatically").action(async (options) => {
456
+ console.log("");
457
+ console.log(import_chalk2.default.bold(" \u26A1 MCP Playbook"));
458
+ console.log(import_chalk2.default.dim(` v${pkg.version}
459
+ `));
460
+ const configPath = import_path3.default.resolve(process.cwd(), options.config);
461
+ if (!import_fs3.default.existsSync(configPath)) {
462
+ console.log(import_chalk2.default.yellow(` Config not found: ${options.config}`));
463
+ console.log(import_chalk2.default.dim(" Run: npx mcp-playbook init\n"));
464
+ process.exit(1);
465
+ }
466
+ try {
467
+ const { startDevServer: startDevServer2 } = (init_dev_server(), __toCommonJS(dev_server_exports));
468
+ await startDevServer2({
469
+ port: parseInt(options.port),
470
+ configPath,
471
+ open: options.open !== false
472
+ });
473
+ } catch (err) {
474
+ console.error(import_chalk2.default.red(`
475
+ Error: ${err.message}
476
+ `));
477
+ process.exit(1);
478
+ }
479
+ });
480
+ program.command("build").description("Build a static documentation site").option("-o, --output <dir>", "Output directory", "playbook-dist").option("-c, --config <path>", "Path to config file", "playbook.config.ts").action(async (options) => {
481
+ console.log(import_chalk2.default.bold("\n \u26A1 MCP Playbook \u2014 Building static site\n"));
482
+ const configPath = import_path3.default.resolve(process.cwd(), options.config);
483
+ if (!import_fs3.default.existsSync(configPath)) {
484
+ console.log(import_chalk2.default.yellow(` Config not found: ${options.config}`));
485
+ console.log(import_chalk2.default.dim(" Run: npx mcp-playbook init\n"));
486
+ process.exit(1);
487
+ }
488
+ try {
489
+ const { buildStaticSite: buildStaticSite2 } = (init_build(), __toCommonJS(build_exports));
490
+ await buildStaticSite2({
491
+ outputDir: import_path3.default.resolve(process.cwd(), options.output),
492
+ configPath
493
+ });
494
+ console.log(import_chalk2.default.green(`
495
+ \u2713 Built to ${options.output}/`));
496
+ console.log(import_chalk2.default.dim(" Deploy this folder to any static host.\n"));
497
+ } catch (err) {
498
+ console.error(import_chalk2.default.red(`
499
+ Build failed: ${err.message}
500
+ `));
501
+ process.exit(1);
502
+ }
503
+ });
504
+ program.parse(process.argv);
505
+ if (!process.argv.slice(2).length) {
506
+ program.outputHelp();
507
+ }
508
+ }
509
+ // Annotate the CommonJS export names for ESM import in node:
510
+ 0 && (module.exports = {
511
+ run
512
+ });
513
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/connector.ts","../../src/server/dev-server.ts","../../src/server/build.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * src/server/connector.ts\n *\n * Connects to MCP servers and discovers their tools.\n * This is the bridge between the user's MCP servers and\n * the Playbook UI.\n *\n * Supports:\n * - stdio transport (spawn a process, talk over stdin/stdout)\n * - HTTP / SSE transport (connect to a running server)\n *\n * Returns a unified DiscoveredServer[] with all tool schemas.\n */\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport type { ServerConfig, ToolExample } from '../config.js'\n\nexport interface DiscoveredTool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n examples: ToolExample[]\n tags: string[]\n}\n\nexport interface DiscoveredServer {\n id: string\n name: string\n url: string\n status: 'connected' | 'disconnected' | 'error'\n version: string\n tools: DiscoveredTool[]\n error?: string\n}\n\n/**\n * Connect to all configured servers and discover their tools.\n * Runs all connections in parallel — slow servers don't block fast ones.\n */\nexport async function discoverAllServers(\n servers: ServerConfig[],\n extraExamples: Record<string, ToolExample[]> = {},\n tags: Record<string, string[]> = {}\n): Promise<DiscoveredServer[]> {\n\n // Build a reverse lookup: tool name → tag names\n const toolTagMap: Record<string, string[]> = {}\n for (const [tag, tools] of Object.entries(tags)) {\n for (const tool of tools) {\n if (!toolTagMap[tool]) toolTagMap[tool] = []\n toolTagMap[tool].push(tag)\n }\n }\n\n // Connect to all servers in parallel\n const results = await Promise.allSettled(\n servers.map((server, i) =>\n connectToServer(server, i, extraExamples, toolTagMap)\n )\n )\n\n return results.map((result, i) => {\n if (result.status === 'fulfilled') {\n return result.value\n } else {\n return {\n id: `server-${i}`,\n name: servers[i].name,\n url: servers[i].url || `${servers[i].command} ${(servers[i].args || []).join(' ')}`,\n status: 'error' as const,\n version: 'unknown',\n tools: [],\n error: result.reason?.message || 'Connection failed'\n }\n }\n })\n}\n\nasync function connectToServer(\n config: ServerConfig,\n index: number,\n extraExamples: Record<string, ToolExample[]>,\n toolTagMap: Record<string, string[]>\n): Promise<DiscoveredServer> {\n\n const client = new Client(\n { name: 'mcp-playbook', version: '0.1.0' },\n { capabilities: {} }\n )\n\n // Create the right transport based on config\n let transport\n\n if (config.transport === 'stdio') {\n if (!config.command) throw new Error(`Server \"${config.name}\" needs a command for stdio transport`)\n transport = new StdioClientTransport({\n command: config.command,\n args: config.args || [],\n env: config.env || {},\n cwd: config.cwd || process.cwd()\n })\n } else if (config.transport === 'http') {\n if (!config.url) throw new Error(`Server \"${config.name}\" needs a url for http transport`)\n transport = new StreamableHTTPClientTransport(new URL(config.url), {\n requestInit: { headers: config.headers || {} }\n })\n } else if (config.transport === 'sse') {\n if (!config.url) throw new Error(`Server \"${config.name}\" needs a url for sse transport`)\n transport = new SSEClientTransport(new URL(config.url), {\n requestInit: { headers: config.headers || {} }\n })\n } else {\n throw new Error(`Unknown transport: ${(config as any).transport}`)\n }\n\n await client.connect(transport)\n\n // Fetch tools from the server\n const { tools } = await client.listTools()\n\n // Fetch server info if available\n const serverInfo = client.getServerVersion()\n\n // Map tools to our format\n const discoveredTools: DiscoveredTool[] = tools.map(tool => ({\n name: tool.name,\n description: tool.description || '',\n inputSchema: tool.inputSchema as Record<string, unknown>,\n examples: extraExamples[tool.name] || [],\n tags: toolTagMap[tool.name] || []\n }))\n\n return {\n id: `server-${index}`,\n name: config.name,\n url: config.url || `${config.command} ${(config.args || []).join(' ')}`,\n status: 'connected',\n version: serverInfo?.version || '1.0.0',\n tools: discoveredTools\n }\n}\n\n/**\n * Execute a tool call against a specific server.\n * Used by the Try tab in the UI.\n */\nexport async function executeTool(\n serverConfig: ServerConfig,\n toolName: string,\n input: Record<string, unknown>\n): Promise<{ result: unknown; duration_ms: number; error?: string }> {\n\n const start = Date.now()\n\n try {\n const client = new Client(\n { name: 'mcp-playbook', version: '0.1.0' },\n { capabilities: {} }\n )\n\n let transport\n if (serverConfig.transport === 'stdio') {\n transport = new StdioClientTransport({\n command: serverConfig.command!,\n args: serverConfig.args || [],\n })\n } else {\n transport = new StreamableHTTPClientTransport(new URL(serverConfig.url!))\n }\n\n await client.connect(transport)\n\n const result = await client.callTool({ name: toolName, arguments: input })\n await client.close()\n\n return {\n result,\n duration_ms: Date.now() - start\n }\n } catch (err: any) {\n return {\n result: null,\n duration_ms: Date.now() - start,\n error: err.message\n }\n }\n}\n","/**\n * src/server/dev-server.ts\n *\n * The development server.\n * Serves the React UI + REST API + WebSocket hot reload.\n */\n\nimport express from 'express'\nimport { createServer } from 'http'\nimport { WebSocketServer } from 'ws'\nimport path from 'path'\nimport fs from 'fs'\nimport chalk from 'chalk'\nimport chokidar from 'chokidar'\nimport open from 'open'\nimport { discoverAllServers, executeTool } from './connector.js'\nimport type { PlaybookConfig } from '../config.js'\n\ninterface DevServerOptions {\n port: number\n configPath: string\n open: boolean\n}\n\n// ── Resolve client dist path robustly ───────────────────────────────\n// Works both when run from source (via ts-node) and from compiled dist/\nfunction resolveClientDist(): string {\n // __dirname in CJS = directory of the compiled file\n // When compiled: dist/server/dev-server.js → dist/client/\n const fromCompiled = path.join(__dirname, '../client')\n if (fs.existsSync(fromCompiled)) return fromCompiled\n\n // Fallback: look relative to package root\n const fromRoot = path.join(__dirname, '../../dist/client')\n if (fs.existsSync(fromRoot)) return fromRoot\n\n return fromCompiled // return anyway — will be checked later\n}\n\n// ── Minimal fallback HTML when client is not built ───────────────────\nfunction fallbackHTML(port: number): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\"/>\n <title>MCP Playbook</title>\n <style>\n body { font-family: monospace; background: #080808; color: #e2e0d8;\n display: flex; align-items: center; justify-content: center;\n height: 100vh; margin: 0; flex-direction: column; gap: 12px; }\n code { background: #1a1a2e; color: #a39cf0; padding: 4px 10px;\n border-radius: 4px; font-size: 14px; }\n p { color: #5a5856; font-size: 13px; }\n </style>\n</head>\n<body>\n <div style=\"font-size:32px\">⚡</div>\n <h2 style=\"margin:0\">MCP Playbook API is running</h2>\n <p>The React UI bundle was not found at <code>dist/client/</code></p>\n <p>Run <code>npm run build:client</code> inside the mcp-playbook library to build it.</p>\n <p>API available at <code>http://localhost:${port}/api/servers</code></p>\n</body>\n</html>`\n}\n\nexport async function startDevServer(options: DevServerOptions) {\n const app = express()\n const server = createServer(app)\n const wss = new WebSocketServer({ server })\n\n app.use(express.json())\n\n // ── Serve static client bundle ──────────────────────────────────\n const clientDist = resolveClientDist()\n const hasClient = fs.existsSync(path.join(clientDist, 'index.html'))\n\n if (hasClient) {\n app.use(express.static(clientDist))\n console.log(chalk.dim(` Client: serving from ${clientDist}`))\n } else {\n console.log(chalk.yellow(` Warning: client bundle not found at ${clientDist}`))\n console.log(chalk.dim(' API is available but UI will show a placeholder'))\n }\n\n // ── Config loader ───────────────────────────────────────────────\n async function loadConfig(): Promise<PlaybookConfig> {\n // Clear module cache for hot reload\n const key = require.resolve(options.configPath)\n delete require.cache[key]\n\n // Try TypeScript config via ts-node/register if available\n try {\n return require(options.configPath).default\n } catch {\n // ts-node not available — try pre-compiled JS version\n const jsPath = options.configPath.replace(/\\.ts$/, '.js')\n if (fs.existsSync(jsPath)) {\n delete require.cache[require.resolve(jsPath)]\n return require(jsPath).default\n }\n throw new Error(\n `Cannot load config: ${options.configPath}\\n` +\n ` Make sure ts-node is installed: npm install -D ts-node\\n` +\n ` Or compile your config to JS first.`\n )\n }\n }\n\n // ── Discover servers ────────────────────────────────────────────\n let cachedServers: Awaited<ReturnType<typeof discoverAllServers>> = []\n let cachedConfig: PlaybookConfig | null = null\n\n async function refreshServers() {\n try {\n const config = await loadConfig()\n cachedConfig = config\n cachedServers = await discoverAllServers(\n config.servers,\n config.examples || {},\n config.tags || {}\n )\n const connected = cachedServers.filter(s => s.status === 'connected').length\n const tools = cachedServers.reduce((acc, s) => acc + s.tools.length, 0)\n console.log(chalk.green(` ✓ ${connected}/${cachedServers.length} servers · ${tools} tools`))\n broadcastToClients({ type: 'reload' })\n } catch (err: any) {\n console.error(chalk.red(` ✗ ${err.message}`))\n }\n }\n\n function broadcastToClients(data: unknown) {\n const msg = JSON.stringify(data)\n wss.clients.forEach(c => { if (c.readyState === 1) c.send(msg) })\n }\n\n // ── REST API ────────────────────────────────────────────────────\n\n app.get('/api/servers', async (_req, res) => {\n try {\n res.json({\n title: cachedConfig?.title || 'MCP Playbook',\n description: cachedConfig?.description || '',\n theme: cachedConfig?.theme || {},\n servers: cachedServers\n })\n } catch (err: any) {\n res.status(500).json({ error: err.message })\n }\n })\n\n app.post('/api/refresh', async (_req, res) => {\n await refreshServers()\n res.json({ ok: true })\n })\n\n app.post('/api/execute', async (req, res) => {\n const { serverId, toolName, input } = req.body\n if (!cachedConfig) return res.status(503).json({ error: 'Config not loaded' })\n\n const idx = parseInt(serverId.replace('server-', ''))\n const serverConfig = cachedConfig.servers[idx]\n if (!serverConfig) return res.status(404).json({ error: `Server not found: ${serverId}` })\n\n const result = await executeTool(serverConfig, toolName, input || {})\n res.json(result)\n })\n\n // Fallback — serve index.html for SPA routing\n app.get('*', (_req, res) => {\n const indexPath = path.join(clientDist, 'index.html')\n if (fs.existsSync(indexPath)) {\n res.sendFile(indexPath)\n } else {\n res.send(fallbackHTML(options.port))\n }\n })\n\n // ── WebSocket ───────────────────────────────────────────────────\n wss.on('connection', (ws) => {\n ws.send(JSON.stringify({ type: 'connected' }))\n })\n\n // ── Config watcher ──────────────────────────────────────────────\n const watcher = chokidar.watch(options.configPath, { ignoreInitial: true })\n watcher.on('change', () => {\n console.log(chalk.dim('\\n Config changed — refreshing...'))\n refreshServers()\n })\n\n // ── Start ───────────────────────────────────────────────────────\n await new Promise<void>((resolve, reject) => {\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n reject(new Error(`Port ${options.port} is already in use. Try: --port ${options.port + 1}`))\n } else {\n reject(err)\n }\n })\n server.listen(options.port, () => resolve())\n })\n\n await refreshServers()\n\n const url = `http://localhost:${options.port}`\n console.log(chalk.dim(`\\n Local: `) + chalk.cyan(url))\n console.log(chalk.dim(` Config: ${options.configPath}\\n`))\n\n if (options.open && hasClient) {\n try { await open(url) } catch {}\n }\n\n return server\n}\n","/**\n * src/server/build.ts\n *\n * Builds a static documentation site from the MCP servers.\n *\n * Output:\n * playbook-dist/\n * index.html\n * assets/\n * data.json ← all server/tool data baked in at build time\n *\n * This lets teams deploy their MCP docs to:\n * - GitHub Pages\n * - Vercel\n * - Netlify\n * - Any static host\n *\n * Users run: mcp-playbook build\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport { discoverAllServers } from './connector.js'\nimport type { PlaybookConfig } from '../config.js'\n\ninterface BuildOptions {\n outputDir: string\n configPath: string\n}\n\nexport async function buildStaticSite(options: BuildOptions) {\n // Load config\n const configModule = await import(options.configPath)\n const config: PlaybookConfig = configModule.default\n\n // Connect to all servers and discover tools\n const servers = await discoverAllServers(\n config.servers,\n config.examples || {},\n config.tags || {}\n )\n\n // Create output directory\n fs.mkdirSync(options.outputDir, { recursive: true })\n\n // Write the data file — the static site reads this instead of hitting the API\n const data = {\n title: config.title || 'MCP Playbook',\n description: config.description || '',\n theme: config.theme || {},\n builtAt: new Date().toISOString(),\n servers\n }\n\n fs.writeFileSync(\n path.join(options.outputDir, 'data.json'),\n JSON.stringify(data, null, 2)\n )\n\n // Copy the static UI bundle\n const clientDist = path.join(\n path.dirname(new URL(import.meta.url).pathname),\n '../../dist/client'\n )\n\n if (fs.existsSync(clientDist)) {\n copyDir(clientDist, options.outputDir)\n }\n\n const toolCount = servers.reduce((acc, s) => acc + s.tools.length, 0)\n console.log(` ${servers.length} servers, ${toolCount} tools documented`)\n}\n\nfunction copyDir(src: string, dest: string) {\n fs.mkdirSync(dest, { recursive: true })\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n const srcPath = path.join(src, entry.name)\n const destPath = path.join(dest, entry.name)\n if (entry.isDirectory()) {\n copyDir(srcPath, destPath)\n } else {\n fs.copyFileSync(srcPath, destPath)\n }\n }\n}\n","/**\n * src/cli/index.ts\n * CLI entry point — dev, build, init commands\n */\n\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport path from 'path'\nimport fs from 'fs'\n\n// Read package.json for version\nconst pkgPath = path.join(__dirname, '../../package.json')\nconst pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))\n\nexport async function run() {\n const program = new Command()\n\n program\n .name('mcp-playbook')\n .description('Playbook for MCP tools — interactive docs and playground')\n .version(pkg.version)\n\n // ── init ─────────────────────────────────────────────────────────\n\n program\n .command('init')\n .description('Create a playbook.config.ts in the current directory')\n .action(() => {\n const configPath = path.resolve(process.cwd(), 'playbook.config.ts')\n\n if (fs.existsSync(configPath)) {\n console.log(chalk.yellow('\\n playbook.config.ts already exists\\n'))\n return\n }\n\n const template = `import { defineConfig } from 'mcp-playbook'\n\nexport default defineConfig({\n title: 'My MCP Playbook',\n description: 'Interactive docs for our MCP tools',\n\n servers: [\n {\n name: 'My API',\n transport: 'stdio',\n command: 'node',\n args: ['./my-mcp-server.js'],\n },\n\n // HTTP transport example:\n // {\n // name: 'Remote API',\n // transport: 'http',\n // url: 'http://localhost:3001/mcp',\n // },\n ],\n\n // Optional: hand-written examples per tool\n examples: {\n // get_user: [\n // {\n // label: 'Basic lookup',\n // input: { userId: 'usr_abc123' }\n // }\n // ]\n },\n\n // Optional: group tools into sidebar sections\n tags: {\n // 'User management': ['get_user', 'create_user'],\n },\n\n theme: {\n primary: '#7F77DD'\n },\n\n port: 4242\n})\n`\n fs.writeFileSync(configPath, template, 'utf8')\n\n console.log(chalk.green('\\n ✓ Created playbook.config.ts'))\n console.log(chalk.dim('\\n Next steps:'))\n console.log(chalk.dim(' 1. Edit playbook.config.ts — point servers at your MCP server'))\n console.log(chalk.dim(' 2. Run: npx mcp-playbook dev'))\n console.log(chalk.dim(' 3. Open: http://localhost:4242\\n'))\n })\n\n // ── dev ───────────────────────────────────────────────────────────\n\n program\n .command('dev')\n .description('Start dev server with hot reload')\n .option('-p, --port <port>', 'Port to run on', '4242')\n .option('-c, --config <path>', 'Path to config file', 'playbook.config.ts')\n .option('--no-open', 'Do not open browser automatically')\n .action(async (options) => {\n console.log('')\n console.log(chalk.bold(' ⚡ MCP Playbook'))\n console.log(chalk.dim(` v${pkg.version}\\n`))\n\n const configPath = path.resolve(process.cwd(), options.config)\n\n if (!fs.existsSync(configPath)) {\n console.log(chalk.yellow(` Config not found: ${options.config}`))\n console.log(chalk.dim(' Run: npx mcp-playbook init\\n'))\n process.exit(1)\n }\n\n try {\n // Lazy import so init command works even if deps not installed\n const { startDevServer } = require('../server/dev-server.js')\n await startDevServer({\n port: parseInt(options.port),\n configPath,\n open: options.open !== false\n })\n } catch (err: any) {\n console.error(chalk.red(`\\n Error: ${err.message}\\n`))\n process.exit(1)\n }\n })\n\n // ── build ─────────────────────────────────────────────────────────\n\n program\n .command('build')\n .description('Build a static documentation site')\n .option('-o, --output <dir>', 'Output directory', 'playbook-dist')\n .option('-c, --config <path>', 'Path to config file', 'playbook.config.ts')\n .action(async (options) => {\n console.log(chalk.bold('\\n ⚡ MCP Playbook — Building static site\\n'))\n\n const configPath = path.resolve(process.cwd(), options.config)\n\n if (!fs.existsSync(configPath)) {\n console.log(chalk.yellow(` Config not found: ${options.config}`))\n console.log(chalk.dim(' Run: npx mcp-playbook init\\n'))\n process.exit(1)\n }\n\n try {\n const { buildStaticSite } = require('../server/build.js')\n await buildStaticSite({\n outputDir: path.resolve(process.cwd(), options.output),\n configPath\n })\n console.log(chalk.green(`\\n ✓ Built to ${options.output}/`))\n console.log(chalk.dim(' Deploy this folder to any static host.\\n'))\n } catch (err: any) {\n console.error(chalk.red(`\\n Build failed: ${err.message}\\n`))\n process.exit(1)\n }\n })\n\n // ── parse ─────────────────────────────────────────────────────────\n\n program.parse(process.argv)\n\n if (!process.argv.slice(2).length) {\n program.outputHelp()\n }\n}\n"],"mappings":"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","names":["path","fs","express","chalk","chokidar","open","fs","path","import_fs","import_path","import_chalk","import_path","import_fs","path","fs","chalk","startDevServer","buildStaticSite"]}