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.
- package/LICENSE +21 -0
- package/README.md +911 -0
- package/bin/mcp-playbook.js +23 -0
- package/bin/postinstall.js +14 -0
- package/dist/cli/index.d.mts +7 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +513 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +498 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/client/assets/index-BtOeKclx.js +40 -0
- package/dist/client/index.html +24 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server/build.d.mts +26 -0
- package/dist/server/build.d.ts +26 -0
- package/dist/server/build.js +168 -0
- package/dist/server/build.js.map +1 -0
- package/dist/server/build.mjs +132 -0
- package/dist/server/build.mjs.map +1 -0
- package/dist/server/connector.d.mts +119 -0
- package/dist/server/connector.d.ts +119 -0
- package/dist/server/connector.js +142 -0
- package/dist/server/connector.js.map +1 -0
- package/dist/server/connector.mjs +116 -0
- package/dist/server/connector.mjs.map +1 -0
- package/dist/server/dev-server.d.mts +16 -0
- package/dist/server/dev-server.d.ts +16 -0
- package/dist/server/dev-server.js +315 -0
- package/dist/server/dev-server.js.map +1 -0
- package/dist/server/dev-server.mjs +287 -0
- package/dist/server/dev-server.mjs.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,168 @@
|
|
|
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 __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/server/build.ts
|
|
31
|
+
var build_exports = {};
|
|
32
|
+
__export(build_exports, {
|
|
33
|
+
buildStaticSite: () => buildStaticSite
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(build_exports);
|
|
36
|
+
var import_fs = __toESM(require("fs"));
|
|
37
|
+
var import_path = __toESM(require("path"));
|
|
38
|
+
|
|
39
|
+
// src/server/connector.ts
|
|
40
|
+
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
41
|
+
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
42
|
+
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
43
|
+
var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
44
|
+
async function discoverAllServers(servers, extraExamples = {}, tags = {}) {
|
|
45
|
+
const toolTagMap = {};
|
|
46
|
+
for (const [tag, tools] of Object.entries(tags)) {
|
|
47
|
+
for (const tool of tools) {
|
|
48
|
+
if (!toolTagMap[tool]) toolTagMap[tool] = [];
|
|
49
|
+
toolTagMap[tool].push(tag);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const results = await Promise.allSettled(
|
|
53
|
+
servers.map(
|
|
54
|
+
(server, i) => connectToServer(server, i, extraExamples, toolTagMap)
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
return results.map((result, i) => {
|
|
58
|
+
if (result.status === "fulfilled") {
|
|
59
|
+
return result.value;
|
|
60
|
+
} else {
|
|
61
|
+
return {
|
|
62
|
+
id: `server-${i}`,
|
|
63
|
+
name: servers[i].name,
|
|
64
|
+
url: servers[i].url || `${servers[i].command} ${(servers[i].args || []).join(" ")}`,
|
|
65
|
+
status: "error",
|
|
66
|
+
version: "unknown",
|
|
67
|
+
tools: [],
|
|
68
|
+
error: result.reason?.message || "Connection failed"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function connectToServer(config, index, extraExamples, toolTagMap) {
|
|
74
|
+
const client = new import_client.Client(
|
|
75
|
+
{ name: "mcp-playbook", version: "0.1.0" },
|
|
76
|
+
{ capabilities: {} }
|
|
77
|
+
);
|
|
78
|
+
let transport;
|
|
79
|
+
if (config.transport === "stdio") {
|
|
80
|
+
if (!config.command) throw new Error(`Server "${config.name}" needs a command for stdio transport`);
|
|
81
|
+
transport = new import_stdio.StdioClientTransport({
|
|
82
|
+
command: config.command,
|
|
83
|
+
args: config.args || [],
|
|
84
|
+
env: config.env || {},
|
|
85
|
+
cwd: config.cwd || process.cwd()
|
|
86
|
+
});
|
|
87
|
+
} else if (config.transport === "http") {
|
|
88
|
+
if (!config.url) throw new Error(`Server "${config.name}" needs a url for http transport`);
|
|
89
|
+
transport = new import_streamableHttp.StreamableHTTPClientTransport(new URL(config.url), {
|
|
90
|
+
requestInit: { headers: config.headers || {} }
|
|
91
|
+
});
|
|
92
|
+
} else if (config.transport === "sse") {
|
|
93
|
+
if (!config.url) throw new Error(`Server "${config.name}" needs a url for sse transport`);
|
|
94
|
+
transport = new import_sse.SSEClientTransport(new URL(config.url), {
|
|
95
|
+
requestInit: { headers: config.headers || {} }
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
throw new Error(`Unknown transport: ${config.transport}`);
|
|
99
|
+
}
|
|
100
|
+
await client.connect(transport);
|
|
101
|
+
const { tools } = await client.listTools();
|
|
102
|
+
const serverInfo = client.getServerVersion();
|
|
103
|
+
const discoveredTools = tools.map((tool) => ({
|
|
104
|
+
name: tool.name,
|
|
105
|
+
description: tool.description || "",
|
|
106
|
+
inputSchema: tool.inputSchema,
|
|
107
|
+
examples: extraExamples[tool.name] || [],
|
|
108
|
+
tags: toolTagMap[tool.name] || []
|
|
109
|
+
}));
|
|
110
|
+
return {
|
|
111
|
+
id: `server-${index}`,
|
|
112
|
+
name: config.name,
|
|
113
|
+
url: config.url || `${config.command} ${(config.args || []).join(" ")}`,
|
|
114
|
+
status: "connected",
|
|
115
|
+
version: serverInfo?.version || "1.0.0",
|
|
116
|
+
tools: discoveredTools
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/server/build.ts
|
|
121
|
+
var import_meta = {};
|
|
122
|
+
async function buildStaticSite(options) {
|
|
123
|
+
const configModule = await import(options.configPath);
|
|
124
|
+
const config = configModule.default;
|
|
125
|
+
const servers = await discoverAllServers(
|
|
126
|
+
config.servers,
|
|
127
|
+
config.examples || {},
|
|
128
|
+
config.tags || {}
|
|
129
|
+
);
|
|
130
|
+
import_fs.default.mkdirSync(options.outputDir, { recursive: true });
|
|
131
|
+
const data = {
|
|
132
|
+
title: config.title || "MCP Playbook",
|
|
133
|
+
description: config.description || "",
|
|
134
|
+
theme: config.theme || {},
|
|
135
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
136
|
+
servers
|
|
137
|
+
};
|
|
138
|
+
import_fs.default.writeFileSync(
|
|
139
|
+
import_path.default.join(options.outputDir, "data.json"),
|
|
140
|
+
JSON.stringify(data, null, 2)
|
|
141
|
+
);
|
|
142
|
+
const clientDist = import_path.default.join(
|
|
143
|
+
import_path.default.dirname(new URL(import_meta.url).pathname),
|
|
144
|
+
"../../dist/client"
|
|
145
|
+
);
|
|
146
|
+
if (import_fs.default.existsSync(clientDist)) {
|
|
147
|
+
copyDir(clientDist, options.outputDir);
|
|
148
|
+
}
|
|
149
|
+
const toolCount = servers.reduce((acc, s) => acc + s.tools.length, 0);
|
|
150
|
+
console.log(` ${servers.length} servers, ${toolCount} tools documented`);
|
|
151
|
+
}
|
|
152
|
+
function copyDir(src, dest) {
|
|
153
|
+
import_fs.default.mkdirSync(dest, { recursive: true });
|
|
154
|
+
for (const entry of import_fs.default.readdirSync(src, { withFileTypes: true })) {
|
|
155
|
+
const srcPath = import_path.default.join(src, entry.name);
|
|
156
|
+
const destPath = import_path.default.join(dest, entry.name);
|
|
157
|
+
if (entry.isDirectory()) {
|
|
158
|
+
copyDir(srcPath, destPath);
|
|
159
|
+
} else {
|
|
160
|
+
import_fs.default.copyFileSync(srcPath, destPath);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
165
|
+
0 && (module.exports = {
|
|
166
|
+
buildStaticSite
|
|
167
|
+
});
|
|
168
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/build.ts","../../src/server/connector.ts"],"sourcesContent":["/**\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/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"],"mappings":"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","names":["fs","path"]}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/server/build.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
// src/server/connector.ts
|
|
6
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
8
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
9
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
10
|
+
async function discoverAllServers(servers, extraExamples = {}, tags = {}) {
|
|
11
|
+
const toolTagMap = {};
|
|
12
|
+
for (const [tag, tools] of Object.entries(tags)) {
|
|
13
|
+
for (const tool of tools) {
|
|
14
|
+
if (!toolTagMap[tool]) toolTagMap[tool] = [];
|
|
15
|
+
toolTagMap[tool].push(tag);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const results = await Promise.allSettled(
|
|
19
|
+
servers.map(
|
|
20
|
+
(server, i) => connectToServer(server, i, extraExamples, toolTagMap)
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
return results.map((result, i) => {
|
|
24
|
+
if (result.status === "fulfilled") {
|
|
25
|
+
return result.value;
|
|
26
|
+
} else {
|
|
27
|
+
return {
|
|
28
|
+
id: `server-${i}`,
|
|
29
|
+
name: servers[i].name,
|
|
30
|
+
url: servers[i].url || `${servers[i].command} ${(servers[i].args || []).join(" ")}`,
|
|
31
|
+
status: "error",
|
|
32
|
+
version: "unknown",
|
|
33
|
+
tools: [],
|
|
34
|
+
error: result.reason?.message || "Connection failed"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function connectToServer(config, index, extraExamples, toolTagMap) {
|
|
40
|
+
const client = new Client(
|
|
41
|
+
{ name: "mcp-playbook", version: "0.1.0" },
|
|
42
|
+
{ capabilities: {} }
|
|
43
|
+
);
|
|
44
|
+
let transport;
|
|
45
|
+
if (config.transport === "stdio") {
|
|
46
|
+
if (!config.command) throw new Error(`Server "${config.name}" needs a command for stdio transport`);
|
|
47
|
+
transport = new StdioClientTransport({
|
|
48
|
+
command: config.command,
|
|
49
|
+
args: config.args || [],
|
|
50
|
+
env: config.env || {},
|
|
51
|
+
cwd: config.cwd || process.cwd()
|
|
52
|
+
});
|
|
53
|
+
} else if (config.transport === "http") {
|
|
54
|
+
if (!config.url) throw new Error(`Server "${config.name}" needs a url for http transport`);
|
|
55
|
+
transport = new StreamableHTTPClientTransport(new URL(config.url), {
|
|
56
|
+
requestInit: { headers: config.headers || {} }
|
|
57
|
+
});
|
|
58
|
+
} else if (config.transport === "sse") {
|
|
59
|
+
if (!config.url) throw new Error(`Server "${config.name}" needs a url for sse transport`);
|
|
60
|
+
transport = new SSEClientTransport(new URL(config.url), {
|
|
61
|
+
requestInit: { headers: config.headers || {} }
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
throw new Error(`Unknown transport: ${config.transport}`);
|
|
65
|
+
}
|
|
66
|
+
await client.connect(transport);
|
|
67
|
+
const { tools } = await client.listTools();
|
|
68
|
+
const serverInfo = client.getServerVersion();
|
|
69
|
+
const discoveredTools = tools.map((tool) => ({
|
|
70
|
+
name: tool.name,
|
|
71
|
+
description: tool.description || "",
|
|
72
|
+
inputSchema: tool.inputSchema,
|
|
73
|
+
examples: extraExamples[tool.name] || [],
|
|
74
|
+
tags: toolTagMap[tool.name] || []
|
|
75
|
+
}));
|
|
76
|
+
return {
|
|
77
|
+
id: `server-${index}`,
|
|
78
|
+
name: config.name,
|
|
79
|
+
url: config.url || `${config.command} ${(config.args || []).join(" ")}`,
|
|
80
|
+
status: "connected",
|
|
81
|
+
version: serverInfo?.version || "1.0.0",
|
|
82
|
+
tools: discoveredTools
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/server/build.ts
|
|
87
|
+
async function buildStaticSite(options) {
|
|
88
|
+
const configModule = await import(options.configPath);
|
|
89
|
+
const config = configModule.default;
|
|
90
|
+
const servers = await discoverAllServers(
|
|
91
|
+
config.servers,
|
|
92
|
+
config.examples || {},
|
|
93
|
+
config.tags || {}
|
|
94
|
+
);
|
|
95
|
+
fs.mkdirSync(options.outputDir, { recursive: true });
|
|
96
|
+
const data = {
|
|
97
|
+
title: config.title || "MCP Playbook",
|
|
98
|
+
description: config.description || "",
|
|
99
|
+
theme: config.theme || {},
|
|
100
|
+
builtAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
101
|
+
servers
|
|
102
|
+
};
|
|
103
|
+
fs.writeFileSync(
|
|
104
|
+
path.join(options.outputDir, "data.json"),
|
|
105
|
+
JSON.stringify(data, null, 2)
|
|
106
|
+
);
|
|
107
|
+
const clientDist = path.join(
|
|
108
|
+
path.dirname(new URL(import.meta.url).pathname),
|
|
109
|
+
"../../dist/client"
|
|
110
|
+
);
|
|
111
|
+
if (fs.existsSync(clientDist)) {
|
|
112
|
+
copyDir(clientDist, options.outputDir);
|
|
113
|
+
}
|
|
114
|
+
const toolCount = servers.reduce((acc, s) => acc + s.tools.length, 0);
|
|
115
|
+
console.log(` ${servers.length} servers, ${toolCount} tools documented`);
|
|
116
|
+
}
|
|
117
|
+
function copyDir(src, dest) {
|
|
118
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
119
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
120
|
+
const srcPath = path.join(src, entry.name);
|
|
121
|
+
const destPath = path.join(dest, entry.name);
|
|
122
|
+
if (entry.isDirectory()) {
|
|
123
|
+
copyDir(srcPath, destPath);
|
|
124
|
+
} else {
|
|
125
|
+
fs.copyFileSync(srcPath, destPath);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
buildStaticSite
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=build.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/build.ts","../../src/server/connector.ts"],"sourcesContent":["/**\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/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"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config.ts
|
|
3
|
+
*
|
|
4
|
+
* Type definitions and the defineConfig() helper.
|
|
5
|
+
*
|
|
6
|
+
* Users write:
|
|
7
|
+
* import { defineConfig } from 'mcp-playbook'
|
|
8
|
+
* export default defineConfig({ ... })
|
|
9
|
+
*
|
|
10
|
+
* Same pattern as Vite, Vitest, etc.
|
|
11
|
+
*/
|
|
12
|
+
interface ServerConfig {
|
|
13
|
+
/** Display name shown in the UI */
|
|
14
|
+
name: string;
|
|
15
|
+
/** How to connect to this MCP server */
|
|
16
|
+
transport: 'stdio' | 'http' | 'sse';
|
|
17
|
+
command?: string;
|
|
18
|
+
args?: string[];
|
|
19
|
+
env?: Record<string, string>;
|
|
20
|
+
cwd?: string;
|
|
21
|
+
url?: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
interface ToolExample {
|
|
25
|
+
/** Short label shown in the Examples tab */
|
|
26
|
+
label: string;
|
|
27
|
+
/** Optional longer description */
|
|
28
|
+
description?: string;
|
|
29
|
+
/** Input values to pre-fill */
|
|
30
|
+
input: Record<string, unknown>;
|
|
31
|
+
/** Optional expected output to show alongside */
|
|
32
|
+
expectedOutput?: unknown;
|
|
33
|
+
}
|
|
34
|
+
interface PlaybookConfig {
|
|
35
|
+
/** Title shown in the UI header */
|
|
36
|
+
title?: string;
|
|
37
|
+
/** Subtitle / description */
|
|
38
|
+
description?: string;
|
|
39
|
+
/** MCP servers to connect to */
|
|
40
|
+
servers: ServerConfig[];
|
|
41
|
+
/**
|
|
42
|
+
* Hand-written examples per tool name.
|
|
43
|
+
* These are merged with any examples found in the server's tool definitions.
|
|
44
|
+
*
|
|
45
|
+
* examples: {
|
|
46
|
+
* get_user: [{ label: 'Basic', input: { userId: 'usr_123' } }]
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
examples?: Record<string, ToolExample[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Group tools under named sections.
|
|
52
|
+
* tags: { 'User management': ['get_user', 'create_user'] }
|
|
53
|
+
*/
|
|
54
|
+
tags?: Record<string, string[]>;
|
|
55
|
+
/** UI theming */
|
|
56
|
+
theme?: {
|
|
57
|
+
primary?: string;
|
|
58
|
+
background?: string;
|
|
59
|
+
font?: string;
|
|
60
|
+
};
|
|
61
|
+
/** Port for dev server. Default: 4242 */
|
|
62
|
+
port?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* defineConfig — gives you TypeScript autocomplete in your config file.
|
|
66
|
+
* This is the primary API users interact with.
|
|
67
|
+
*
|
|
68
|
+
* Usage:
|
|
69
|
+
* import { defineConfig } from 'mcp-playbook'
|
|
70
|
+
* export default defineConfig({ servers: [...] })
|
|
71
|
+
*/
|
|
72
|
+
declare function defineConfig(config: PlaybookConfig): PlaybookConfig;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* src/server/connector.ts
|
|
76
|
+
*
|
|
77
|
+
* Connects to MCP servers and discovers their tools.
|
|
78
|
+
* This is the bridge between the user's MCP servers and
|
|
79
|
+
* the Playbook UI.
|
|
80
|
+
*
|
|
81
|
+
* Supports:
|
|
82
|
+
* - stdio transport (spawn a process, talk over stdin/stdout)
|
|
83
|
+
* - HTTP / SSE transport (connect to a running server)
|
|
84
|
+
*
|
|
85
|
+
* Returns a unified DiscoveredServer[] with all tool schemas.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
interface DiscoveredTool {
|
|
89
|
+
name: string;
|
|
90
|
+
description: string;
|
|
91
|
+
inputSchema: Record<string, unknown>;
|
|
92
|
+
examples: ToolExample[];
|
|
93
|
+
tags: string[];
|
|
94
|
+
}
|
|
95
|
+
interface DiscoveredServer {
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
url: string;
|
|
99
|
+
status: 'connected' | 'disconnected' | 'error';
|
|
100
|
+
version: string;
|
|
101
|
+
tools: DiscoveredTool[];
|
|
102
|
+
error?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Connect to all configured servers and discover their tools.
|
|
106
|
+
* Runs all connections in parallel — slow servers don't block fast ones.
|
|
107
|
+
*/
|
|
108
|
+
declare function discoverAllServers(servers: ServerConfig[], extraExamples?: Record<string, ToolExample[]>, tags?: Record<string, string[]>): Promise<DiscoveredServer[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Execute a tool call against a specific server.
|
|
111
|
+
* Used by the Try tab in the UI.
|
|
112
|
+
*/
|
|
113
|
+
declare function executeTool(serverConfig: ServerConfig, toolName: string, input: Record<string, unknown>): Promise<{
|
|
114
|
+
result: unknown;
|
|
115
|
+
duration_ms: number;
|
|
116
|
+
error?: string;
|
|
117
|
+
}>;
|
|
118
|
+
|
|
119
|
+
export { type DiscoveredServer, type DiscoveredTool, type PlaybookConfig as P, type ServerConfig as S, type ToolExample as T, defineConfig as d, discoverAllServers, executeTool };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config.ts
|
|
3
|
+
*
|
|
4
|
+
* Type definitions and the defineConfig() helper.
|
|
5
|
+
*
|
|
6
|
+
* Users write:
|
|
7
|
+
* import { defineConfig } from 'mcp-playbook'
|
|
8
|
+
* export default defineConfig({ ... })
|
|
9
|
+
*
|
|
10
|
+
* Same pattern as Vite, Vitest, etc.
|
|
11
|
+
*/
|
|
12
|
+
interface ServerConfig {
|
|
13
|
+
/** Display name shown in the UI */
|
|
14
|
+
name: string;
|
|
15
|
+
/** How to connect to this MCP server */
|
|
16
|
+
transport: 'stdio' | 'http' | 'sse';
|
|
17
|
+
command?: string;
|
|
18
|
+
args?: string[];
|
|
19
|
+
env?: Record<string, string>;
|
|
20
|
+
cwd?: string;
|
|
21
|
+
url?: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
interface ToolExample {
|
|
25
|
+
/** Short label shown in the Examples tab */
|
|
26
|
+
label: string;
|
|
27
|
+
/** Optional longer description */
|
|
28
|
+
description?: string;
|
|
29
|
+
/** Input values to pre-fill */
|
|
30
|
+
input: Record<string, unknown>;
|
|
31
|
+
/** Optional expected output to show alongside */
|
|
32
|
+
expectedOutput?: unknown;
|
|
33
|
+
}
|
|
34
|
+
interface PlaybookConfig {
|
|
35
|
+
/** Title shown in the UI header */
|
|
36
|
+
title?: string;
|
|
37
|
+
/** Subtitle / description */
|
|
38
|
+
description?: string;
|
|
39
|
+
/** MCP servers to connect to */
|
|
40
|
+
servers: ServerConfig[];
|
|
41
|
+
/**
|
|
42
|
+
* Hand-written examples per tool name.
|
|
43
|
+
* These are merged with any examples found in the server's tool definitions.
|
|
44
|
+
*
|
|
45
|
+
* examples: {
|
|
46
|
+
* get_user: [{ label: 'Basic', input: { userId: 'usr_123' } }]
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
examples?: Record<string, ToolExample[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Group tools under named sections.
|
|
52
|
+
* tags: { 'User management': ['get_user', 'create_user'] }
|
|
53
|
+
*/
|
|
54
|
+
tags?: Record<string, string[]>;
|
|
55
|
+
/** UI theming */
|
|
56
|
+
theme?: {
|
|
57
|
+
primary?: string;
|
|
58
|
+
background?: string;
|
|
59
|
+
font?: string;
|
|
60
|
+
};
|
|
61
|
+
/** Port for dev server. Default: 4242 */
|
|
62
|
+
port?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* defineConfig — gives you TypeScript autocomplete in your config file.
|
|
66
|
+
* This is the primary API users interact with.
|
|
67
|
+
*
|
|
68
|
+
* Usage:
|
|
69
|
+
* import { defineConfig } from 'mcp-playbook'
|
|
70
|
+
* export default defineConfig({ servers: [...] })
|
|
71
|
+
*/
|
|
72
|
+
declare function defineConfig(config: PlaybookConfig): PlaybookConfig;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* src/server/connector.ts
|
|
76
|
+
*
|
|
77
|
+
* Connects to MCP servers and discovers their tools.
|
|
78
|
+
* This is the bridge between the user's MCP servers and
|
|
79
|
+
* the Playbook UI.
|
|
80
|
+
*
|
|
81
|
+
* Supports:
|
|
82
|
+
* - stdio transport (spawn a process, talk over stdin/stdout)
|
|
83
|
+
* - HTTP / SSE transport (connect to a running server)
|
|
84
|
+
*
|
|
85
|
+
* Returns a unified DiscoveredServer[] with all tool schemas.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
interface DiscoveredTool {
|
|
89
|
+
name: string;
|
|
90
|
+
description: string;
|
|
91
|
+
inputSchema: Record<string, unknown>;
|
|
92
|
+
examples: ToolExample[];
|
|
93
|
+
tags: string[];
|
|
94
|
+
}
|
|
95
|
+
interface DiscoveredServer {
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
url: string;
|
|
99
|
+
status: 'connected' | 'disconnected' | 'error';
|
|
100
|
+
version: string;
|
|
101
|
+
tools: DiscoveredTool[];
|
|
102
|
+
error?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Connect to all configured servers and discover their tools.
|
|
106
|
+
* Runs all connections in parallel — slow servers don't block fast ones.
|
|
107
|
+
*/
|
|
108
|
+
declare function discoverAllServers(servers: ServerConfig[], extraExamples?: Record<string, ToolExample[]>, tags?: Record<string, string[]>): Promise<DiscoveredServer[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Execute a tool call against a specific server.
|
|
111
|
+
* Used by the Try tab in the UI.
|
|
112
|
+
*/
|
|
113
|
+
declare function executeTool(serverConfig: ServerConfig, toolName: string, input: Record<string, unknown>): Promise<{
|
|
114
|
+
result: unknown;
|
|
115
|
+
duration_ms: number;
|
|
116
|
+
error?: string;
|
|
117
|
+
}>;
|
|
118
|
+
|
|
119
|
+
export { type DiscoveredServer, type DiscoveredTool, type PlaybookConfig as P, type ServerConfig as S, type ToolExample as T, defineConfig as d, discoverAllServers, executeTool };
|