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,498 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ // src/server/connector.ts
29
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
30
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
31
+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
32
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
33
+ async function discoverAllServers(servers, extraExamples = {}, tags = {}) {
34
+ const toolTagMap = {};
35
+ for (const [tag, tools] of Object.entries(tags)) {
36
+ for (const tool of tools) {
37
+ if (!toolTagMap[tool]) toolTagMap[tool] = [];
38
+ toolTagMap[tool].push(tag);
39
+ }
40
+ }
41
+ const results = await Promise.allSettled(
42
+ servers.map(
43
+ (server, i) => connectToServer(server, i, extraExamples, toolTagMap)
44
+ )
45
+ );
46
+ return results.map((result, i) => {
47
+ if (result.status === "fulfilled") {
48
+ return result.value;
49
+ } else {
50
+ return {
51
+ id: `server-${i}`,
52
+ name: servers[i].name,
53
+ url: servers[i].url || `${servers[i].command} ${(servers[i].args || []).join(" ")}`,
54
+ status: "error",
55
+ version: "unknown",
56
+ tools: [],
57
+ error: result.reason?.message || "Connection failed"
58
+ };
59
+ }
60
+ });
61
+ }
62
+ async function connectToServer(config, index, extraExamples, toolTagMap) {
63
+ const client = new Client(
64
+ { name: "mcp-playbook", version: "0.1.0" },
65
+ { capabilities: {} }
66
+ );
67
+ let transport;
68
+ if (config.transport === "stdio") {
69
+ if (!config.command) throw new Error(`Server "${config.name}" needs a command for stdio transport`);
70
+ transport = new StdioClientTransport({
71
+ command: config.command,
72
+ args: config.args || [],
73
+ env: config.env || {},
74
+ cwd: config.cwd || process.cwd()
75
+ });
76
+ } else if (config.transport === "http") {
77
+ if (!config.url) throw new Error(`Server "${config.name}" needs a url for http transport`);
78
+ transport = new StreamableHTTPClientTransport(new URL(config.url), {
79
+ requestInit: { headers: config.headers || {} }
80
+ });
81
+ } else if (config.transport === "sse") {
82
+ if (!config.url) throw new Error(`Server "${config.name}" needs a url for sse transport`);
83
+ transport = new SSEClientTransport(new URL(config.url), {
84
+ requestInit: { headers: config.headers || {} }
85
+ });
86
+ } else {
87
+ throw new Error(`Unknown transport: ${config.transport}`);
88
+ }
89
+ await client.connect(transport);
90
+ const { tools } = await client.listTools();
91
+ const serverInfo = client.getServerVersion();
92
+ const discoveredTools = tools.map((tool) => ({
93
+ name: tool.name,
94
+ description: tool.description || "",
95
+ inputSchema: tool.inputSchema,
96
+ examples: extraExamples[tool.name] || [],
97
+ tags: toolTagMap[tool.name] || []
98
+ }));
99
+ return {
100
+ id: `server-${index}`,
101
+ name: config.name,
102
+ url: config.url || `${config.command} ${(config.args || []).join(" ")}`,
103
+ status: "connected",
104
+ version: serverInfo?.version || "1.0.0",
105
+ tools: discoveredTools
106
+ };
107
+ }
108
+ async function executeTool(serverConfig, toolName, input) {
109
+ const start = Date.now();
110
+ try {
111
+ const client = new Client(
112
+ { name: "mcp-playbook", version: "0.1.0" },
113
+ { capabilities: {} }
114
+ );
115
+ let transport;
116
+ if (serverConfig.transport === "stdio") {
117
+ transport = new StdioClientTransport({
118
+ command: serverConfig.command,
119
+ args: serverConfig.args || []
120
+ });
121
+ } else {
122
+ transport = new StreamableHTTPClientTransport(new URL(serverConfig.url));
123
+ }
124
+ await client.connect(transport);
125
+ const result = await client.callTool({ name: toolName, arguments: input });
126
+ await client.close();
127
+ return {
128
+ result,
129
+ duration_ms: Date.now() - start
130
+ };
131
+ } catch (err) {
132
+ return {
133
+ result: null,
134
+ duration_ms: Date.now() - start,
135
+ error: err.message
136
+ };
137
+ }
138
+ }
139
+ var init_connector = __esm({
140
+ "src/server/connector.ts"() {
141
+ "use strict";
142
+ }
143
+ });
144
+
145
+ // src/server/dev-server.ts
146
+ var dev_server_exports = {};
147
+ __export(dev_server_exports, {
148
+ startDevServer: () => startDevServer
149
+ });
150
+ import express from "express";
151
+ import { createServer } from "http";
152
+ import { WebSocketServer } from "ws";
153
+ import path from "path";
154
+ import fs from "fs";
155
+ import chalk from "chalk";
156
+ import chokidar from "chokidar";
157
+ import open from "open";
158
+ function resolveClientDist() {
159
+ const fromCompiled = path.join(__dirname, "../client");
160
+ if (fs.existsSync(fromCompiled)) return fromCompiled;
161
+ const fromRoot = path.join(__dirname, "../../dist/client");
162
+ if (fs.existsSync(fromRoot)) return fromRoot;
163
+ return fromCompiled;
164
+ }
165
+ function fallbackHTML(port) {
166
+ return `<!DOCTYPE html>
167
+ <html>
168
+ <head>
169
+ <meta charset="UTF-8"/>
170
+ <title>MCP Playbook</title>
171
+ <style>
172
+ body { font-family: monospace; background: #080808; color: #e2e0d8;
173
+ display: flex; align-items: center; justify-content: center;
174
+ height: 100vh; margin: 0; flex-direction: column; gap: 12px; }
175
+ code { background: #1a1a2e; color: #a39cf0; padding: 4px 10px;
176
+ border-radius: 4px; font-size: 14px; }
177
+ p { color: #5a5856; font-size: 13px; }
178
+ </style>
179
+ </head>
180
+ <body>
181
+ <div style="font-size:32px">\u26A1</div>
182
+ <h2 style="margin:0">MCP Playbook API is running</h2>
183
+ <p>The React UI bundle was not found at <code>dist/client/</code></p>
184
+ <p>Run <code>npm run build:client</code> inside the mcp-playbook library to build it.</p>
185
+ <p>API available at <code>http://localhost:${port}/api/servers</code></p>
186
+ </body>
187
+ </html>`;
188
+ }
189
+ async function startDevServer(options) {
190
+ const app = express();
191
+ const server = createServer(app);
192
+ const wss = new WebSocketServer({ server });
193
+ app.use(express.json());
194
+ const clientDist = resolveClientDist();
195
+ const hasClient = fs.existsSync(path.join(clientDist, "index.html"));
196
+ if (hasClient) {
197
+ app.use(express.static(clientDist));
198
+ console.log(chalk.dim(` Client: serving from ${clientDist}`));
199
+ } else {
200
+ console.log(chalk.yellow(` Warning: client bundle not found at ${clientDist}`));
201
+ console.log(chalk.dim(" API is available but UI will show a placeholder"));
202
+ }
203
+ async function loadConfig() {
204
+ const key = __require.resolve(options.configPath);
205
+ delete __require.cache[key];
206
+ try {
207
+ return __require(options.configPath).default;
208
+ } catch {
209
+ const jsPath = options.configPath.replace(/\.ts$/, ".js");
210
+ if (fs.existsSync(jsPath)) {
211
+ delete __require.cache[__require.resolve(jsPath)];
212
+ return __require(jsPath).default;
213
+ }
214
+ throw new Error(
215
+ `Cannot load config: ${options.configPath}
216
+ Make sure ts-node is installed: npm install -D ts-node
217
+ Or compile your config to JS first.`
218
+ );
219
+ }
220
+ }
221
+ let cachedServers = [];
222
+ let cachedConfig = null;
223
+ async function refreshServers() {
224
+ try {
225
+ const config = await loadConfig();
226
+ cachedConfig = config;
227
+ cachedServers = await discoverAllServers(
228
+ config.servers,
229
+ config.examples || {},
230
+ config.tags || {}
231
+ );
232
+ const connected = cachedServers.filter((s) => s.status === "connected").length;
233
+ const tools = cachedServers.reduce((acc, s) => acc + s.tools.length, 0);
234
+ console.log(chalk.green(` \u2713 ${connected}/${cachedServers.length} servers \xB7 ${tools} tools`));
235
+ broadcastToClients({ type: "reload" });
236
+ } catch (err) {
237
+ console.error(chalk.red(` \u2717 ${err.message}`));
238
+ }
239
+ }
240
+ function broadcastToClients(data) {
241
+ const msg = JSON.stringify(data);
242
+ wss.clients.forEach((c) => {
243
+ if (c.readyState === 1) c.send(msg);
244
+ });
245
+ }
246
+ app.get("/api/servers", async (_req, res) => {
247
+ try {
248
+ res.json({
249
+ title: cachedConfig?.title || "MCP Playbook",
250
+ description: cachedConfig?.description || "",
251
+ theme: cachedConfig?.theme || {},
252
+ servers: cachedServers
253
+ });
254
+ } catch (err) {
255
+ res.status(500).json({ error: err.message });
256
+ }
257
+ });
258
+ app.post("/api/refresh", async (_req, res) => {
259
+ await refreshServers();
260
+ res.json({ ok: true });
261
+ });
262
+ app.post("/api/execute", async (req, res) => {
263
+ const { serverId, toolName, input } = req.body;
264
+ if (!cachedConfig) return res.status(503).json({ error: "Config not loaded" });
265
+ const idx = parseInt(serverId.replace("server-", ""));
266
+ const serverConfig = cachedConfig.servers[idx];
267
+ if (!serverConfig) return res.status(404).json({ error: `Server not found: ${serverId}` });
268
+ const result = await executeTool(serverConfig, toolName, input || {});
269
+ res.json(result);
270
+ });
271
+ app.get("*", (_req, res) => {
272
+ const indexPath = path.join(clientDist, "index.html");
273
+ if (fs.existsSync(indexPath)) {
274
+ res.sendFile(indexPath);
275
+ } else {
276
+ res.send(fallbackHTML(options.port));
277
+ }
278
+ });
279
+ wss.on("connection", (ws) => {
280
+ ws.send(JSON.stringify({ type: "connected" }));
281
+ });
282
+ const watcher = chokidar.watch(options.configPath, { ignoreInitial: true });
283
+ watcher.on("change", () => {
284
+ console.log(chalk.dim("\n Config changed \u2014 refreshing..."));
285
+ refreshServers();
286
+ });
287
+ await new Promise((resolve, reject) => {
288
+ server.on("error", (err) => {
289
+ if (err.code === "EADDRINUSE") {
290
+ reject(new Error(`Port ${options.port} is already in use. Try: --port ${options.port + 1}`));
291
+ } else {
292
+ reject(err);
293
+ }
294
+ });
295
+ server.listen(options.port, () => resolve());
296
+ });
297
+ await refreshServers();
298
+ const url = `http://localhost:${options.port}`;
299
+ console.log(chalk.dim(`
300
+ Local: `) + chalk.cyan(url));
301
+ console.log(chalk.dim(` Config: ${options.configPath}
302
+ `));
303
+ if (options.open && hasClient) {
304
+ try {
305
+ await open(url);
306
+ } catch {
307
+ }
308
+ }
309
+ return server;
310
+ }
311
+ var init_dev_server = __esm({
312
+ "src/server/dev-server.ts"() {
313
+ "use strict";
314
+ init_connector();
315
+ }
316
+ });
317
+
318
+ // src/server/build.ts
319
+ var build_exports = {};
320
+ __export(build_exports, {
321
+ buildStaticSite: () => buildStaticSite
322
+ });
323
+ import fs2 from "fs";
324
+ import path2 from "path";
325
+ async function buildStaticSite(options) {
326
+ const configModule = await import(options.configPath);
327
+ const config = configModule.default;
328
+ const servers = await discoverAllServers(
329
+ config.servers,
330
+ config.examples || {},
331
+ config.tags || {}
332
+ );
333
+ fs2.mkdirSync(options.outputDir, { recursive: true });
334
+ const data = {
335
+ title: config.title || "MCP Playbook",
336
+ description: config.description || "",
337
+ theme: config.theme || {},
338
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
339
+ servers
340
+ };
341
+ fs2.writeFileSync(
342
+ path2.join(options.outputDir, "data.json"),
343
+ JSON.stringify(data, null, 2)
344
+ );
345
+ const clientDist = path2.join(
346
+ path2.dirname(new URL(import.meta.url).pathname),
347
+ "../../dist/client"
348
+ );
349
+ if (fs2.existsSync(clientDist)) {
350
+ copyDir(clientDist, options.outputDir);
351
+ }
352
+ const toolCount = servers.reduce((acc, s) => acc + s.tools.length, 0);
353
+ console.log(` ${servers.length} servers, ${toolCount} tools documented`);
354
+ }
355
+ function copyDir(src, dest) {
356
+ fs2.mkdirSync(dest, { recursive: true });
357
+ for (const entry of fs2.readdirSync(src, { withFileTypes: true })) {
358
+ const srcPath = path2.join(src, entry.name);
359
+ const destPath = path2.join(dest, entry.name);
360
+ if (entry.isDirectory()) {
361
+ copyDir(srcPath, destPath);
362
+ } else {
363
+ fs2.copyFileSync(srcPath, destPath);
364
+ }
365
+ }
366
+ }
367
+ var init_build = __esm({
368
+ "src/server/build.ts"() {
369
+ "use strict";
370
+ init_connector();
371
+ }
372
+ });
373
+
374
+ // src/cli/index.ts
375
+ import { Command } from "commander";
376
+ import chalk2 from "chalk";
377
+ import path3 from "path";
378
+ import fs3 from "fs";
379
+ var pkgPath = path3.join(__dirname, "../../package.json");
380
+ var pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
381
+ async function run() {
382
+ const program = new Command();
383
+ program.name("mcp-playbook").description("Playbook for MCP tools \u2014 interactive docs and playground").version(pkg.version);
384
+ program.command("init").description("Create a playbook.config.ts in the current directory").action(() => {
385
+ const configPath = path3.resolve(process.cwd(), "playbook.config.ts");
386
+ if (fs3.existsSync(configPath)) {
387
+ console.log(chalk2.yellow("\n playbook.config.ts already exists\n"));
388
+ return;
389
+ }
390
+ const template = `import { defineConfig } from 'mcp-playbook'
391
+
392
+ export default defineConfig({
393
+ title: 'My MCP Playbook',
394
+ description: 'Interactive docs for our MCP tools',
395
+
396
+ servers: [
397
+ {
398
+ name: 'My API',
399
+ transport: 'stdio',
400
+ command: 'node',
401
+ args: ['./my-mcp-server.js'],
402
+ },
403
+
404
+ // HTTP transport example:
405
+ // {
406
+ // name: 'Remote API',
407
+ // transport: 'http',
408
+ // url: 'http://localhost:3001/mcp',
409
+ // },
410
+ ],
411
+
412
+ // Optional: hand-written examples per tool
413
+ examples: {
414
+ // get_user: [
415
+ // {
416
+ // label: 'Basic lookup',
417
+ // input: { userId: 'usr_abc123' }
418
+ // }
419
+ // ]
420
+ },
421
+
422
+ // Optional: group tools into sidebar sections
423
+ tags: {
424
+ // 'User management': ['get_user', 'create_user'],
425
+ },
426
+
427
+ theme: {
428
+ primary: '#7F77DD'
429
+ },
430
+
431
+ port: 4242
432
+ })
433
+ `;
434
+ fs3.writeFileSync(configPath, template, "utf8");
435
+ console.log(chalk2.green("\n \u2713 Created playbook.config.ts"));
436
+ console.log(chalk2.dim("\n Next steps:"));
437
+ console.log(chalk2.dim(" 1. Edit playbook.config.ts \u2014 point servers at your MCP server"));
438
+ console.log(chalk2.dim(" 2. Run: npx mcp-playbook dev"));
439
+ console.log(chalk2.dim(" 3. Open: http://localhost:4242\n"));
440
+ });
441
+ 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) => {
442
+ console.log("");
443
+ console.log(chalk2.bold(" \u26A1 MCP Playbook"));
444
+ console.log(chalk2.dim(` v${pkg.version}
445
+ `));
446
+ const configPath = path3.resolve(process.cwd(), options.config);
447
+ if (!fs3.existsSync(configPath)) {
448
+ console.log(chalk2.yellow(` Config not found: ${options.config}`));
449
+ console.log(chalk2.dim(" Run: npx mcp-playbook init\n"));
450
+ process.exit(1);
451
+ }
452
+ try {
453
+ const { startDevServer: startDevServer2 } = (init_dev_server(), __toCommonJS(dev_server_exports));
454
+ await startDevServer2({
455
+ port: parseInt(options.port),
456
+ configPath,
457
+ open: options.open !== false
458
+ });
459
+ } catch (err) {
460
+ console.error(chalk2.red(`
461
+ Error: ${err.message}
462
+ `));
463
+ process.exit(1);
464
+ }
465
+ });
466
+ 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) => {
467
+ console.log(chalk2.bold("\n \u26A1 MCP Playbook \u2014 Building static site\n"));
468
+ const configPath = path3.resolve(process.cwd(), options.config);
469
+ if (!fs3.existsSync(configPath)) {
470
+ console.log(chalk2.yellow(` Config not found: ${options.config}`));
471
+ console.log(chalk2.dim(" Run: npx mcp-playbook init\n"));
472
+ process.exit(1);
473
+ }
474
+ try {
475
+ const { buildStaticSite: buildStaticSite2 } = (init_build(), __toCommonJS(build_exports));
476
+ await buildStaticSite2({
477
+ outputDir: path3.resolve(process.cwd(), options.output),
478
+ configPath
479
+ });
480
+ console.log(chalk2.green(`
481
+ \u2713 Built to ${options.output}/`));
482
+ console.log(chalk2.dim(" Deploy this folder to any static host.\n"));
483
+ } catch (err) {
484
+ console.error(chalk2.red(`
485
+ Build failed: ${err.message}
486
+ `));
487
+ process.exit(1);
488
+ }
489
+ });
490
+ program.parse(process.argv);
491
+ if (!process.argv.slice(2).length) {
492
+ program.outputHelp();
493
+ }
494
+ }
495
+ export {
496
+ run
497
+ };
498
+ //# sourceMappingURL=index.mjs.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":["fs","path","chalk","path","fs","startDevServer","buildStaticSite"]}