@mpurdon/mcp-servers 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Purdon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @mpurdon/mcp-servers
2
+
3
+ The installer/configurator for the [@mpurdon MCP servers](https://github.com/mpurdon/mcp-servers).
4
+ It registers any combination of the servers into **Claude Desktop**, **Claude Code**,
5
+ or a **Claude Cowork** workspace — writing the right config file, idempotently.
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ # interactive: pick a host, pick servers, enter credentials, write config
11
+ npx @mpurdon/mcp-servers configure
12
+
13
+ # preview the exact changes without writing anything
14
+ npx @mpurdon/mcp-servers configure --dry-run
15
+
16
+ # list available servers
17
+ npx @mpurdon/mcp-servers list
18
+ ```
19
+
20
+ ## Public and private servers
21
+
22
+ The configurator offers two tiers in one selectable list:
23
+
24
+ - **Public servers** — the npm-published `@mpurdon/mcp-*` packages, launched via `npx`.
25
+ - **Private/local servers** — proprietary or work-specific servers whose code lives
26
+ in a separate private repo. They register themselves by dropping a descriptor in
27
+ `~/.mpurdon-mcp/servers.d/<key>.json` (each private repo provides its own
28
+ `*-register` command). The configurator auto-discovers these and launches them by
29
+ their local path. No proprietary detail ever lives in this public repo — only the
30
+ descriptor on your machine.
31
+
32
+ So `configure` shows everything you have installed — public and private — as one list,
33
+ marking private ones `(private)`.
34
+
35
+ ## What it does
36
+
37
+ 1. **Detects** your installed Claude host(s) and lets you choose one.
38
+ 2. Lets you **multi-select** which servers to enable (public + discovered private).
39
+ 3. **Prompts** for each server's required credentials (secrets are masked) — sourced
40
+ from the server registry, so the prompts always match what each server needs.
41
+ 4. **Merges** the entries into the host's `mcpServers` config without clobbering
42
+ anything else in the file. Re-running is safe (idempotent); existing entries
43
+ are only touched if they differ, and you're warned before any overwrite.
44
+ 5. Prints **follow-up steps** for servers that need extra setup (e.g. the FreshBooks
45
+ OAuth flow, or the MongoDB config file).
46
+
47
+ ## Config locations
48
+
49
+ | Host | File |
50
+ | ------------------------ | ----------------------------------------------------------------- |
51
+ | Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
52
+ | Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
53
+ | Claude Desktop (Linux) | `~/.config/Claude/claude_desktop_config.json` |
54
+ | Claude Code | `~/.claude.json` |
55
+ | Claude Cowork | `<workspace>/.mcp.json` |
56
+
57
+ After configuring, restart the host so it picks up the new servers.
@@ -0,0 +1,59 @@
1
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ /** Read and parse an MCP config file, tolerating a missing or empty file. */
4
+ export function readConfig(path) {
5
+ if (!existsSync(path))
6
+ return {};
7
+ const raw = readFileSync(path, "utf8").trim();
8
+ if (!raw)
9
+ return {};
10
+ try {
11
+ const parsed = JSON.parse(raw);
12
+ return parsed && typeof parsed === "object" ? parsed : {};
13
+ }
14
+ catch (err) {
15
+ throw new Error(`Existing config at ${path} is not valid JSON (${err.message}). ` +
16
+ "Fix or remove it before re-running.");
17
+ }
18
+ }
19
+ function entriesEqual(a, b) {
20
+ return JSON.stringify(a) === JSON.stringify(b);
21
+ }
22
+ /** Compute what would change, without mutating anything. */
23
+ export function planChanges(config, desired) {
24
+ const existing = config.mcpServers ?? {};
25
+ return Object.entries(desired).map(([key, entry]) => {
26
+ const prev = existing[key];
27
+ let action;
28
+ if (!prev)
29
+ action = "add";
30
+ else if (entriesEqual(prev, entry))
31
+ action = "unchanged";
32
+ else
33
+ action = "update";
34
+ return { key, action, entry };
35
+ });
36
+ }
37
+ /** Apply desired entries into the config object (in place) and return it. */
38
+ export function applyChanges(config, desired) {
39
+ config.mcpServers = { ...(config.mcpServers ?? {}), ...desired };
40
+ return config;
41
+ }
42
+ /** Serialize and write the config, creating parent dirs as needed. */
43
+ export function writeConfig(path, config) {
44
+ mkdirSync(dirname(path), { recursive: true });
45
+ writeFileSync(path, JSON.stringify(config, null, 2) + "\n", "utf8");
46
+ }
47
+ /** A human-readable diff of planned changes for --dry-run and confirmations. */
48
+ export function renderPlan(path, changes) {
49
+ const lines = [`Target: ${path}`, ""];
50
+ for (const c of changes) {
51
+ const marker = c.action === "add" ? "+" : c.action === "update" ? "~" : "=";
52
+ lines.push(` ${marker} ${c.key} (${c.action})`);
53
+ const envKeys = Object.keys(c.entry.env ?? {});
54
+ const envDesc = envKeys.length > 0 ? `, env: ${envKeys.join(", ")}` : ", env: (none)";
55
+ lines.push(` ${c.entry.command} ${c.entry.args.join(" ")}${envDesc}`);
56
+ }
57
+ return lines.join("\n");
58
+ }
59
+ //# sourceMappingURL=config-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../src/config-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import * as p from "@clack/prompts";
2
+ import { HOSTS, getHost } from "./hosts.js";
3
+ import { BUILTIN_SERVERS } from "./registry.js";
4
+ import { allServers } from "./discovery.js";
5
+ import { readConfig, planChanges, applyChanges, writeConfig, renderPlan, } from "./config-file.js";
6
+ function bail(message = "Cancelled.") {
7
+ p.cancel(message);
8
+ process.exit(1);
9
+ }
10
+ function ensure(value) {
11
+ if (p.isCancel(value))
12
+ bail();
13
+ return value;
14
+ }
15
+ function entryFor(server, env) {
16
+ // Local/private servers carry an explicit launch command (e.g. a built dist
17
+ // on disk); public servers run from npm via npx.
18
+ const entry = server.launch
19
+ ? { command: server.launch.command, args: [...server.launch.args] }
20
+ : { command: "npx", args: ["-y", server.packageName] };
21
+ if (Object.keys(env).length > 0)
22
+ entry.env = env;
23
+ return entry;
24
+ }
25
+ export async function configure(opts) {
26
+ p.intro("@mpurdon/mcp-servers configure");
27
+ // 1. Pick a host (default to a detected one).
28
+ const detected = HOSTS.find((h) => h.id !== "cowork" && h.detect());
29
+ const hostId = ensure(await p.select({
30
+ message: "Which Claude host do you want to configure?",
31
+ initialValue: detected?.id ?? "desktop",
32
+ options: HOSTS.map((h) => ({
33
+ value: h.id,
34
+ label: h.title,
35
+ hint: h.id !== "cowork" && h.detect() ? "detected" : undefined,
36
+ })),
37
+ }));
38
+ const host = getHost(hostId);
39
+ // Resolve the target config path (Cowork needs a workspace dir).
40
+ let workspaceDir;
41
+ if (hostId === "cowork") {
42
+ workspaceDir = ensure(await p.text({
43
+ message: "Path to the Cowork workspace (its .mcp.json lives here):",
44
+ placeholder: process.cwd(),
45
+ defaultValue: process.cwd(),
46
+ }));
47
+ }
48
+ const targetPath = host.resolvePath({ workspaceDir });
49
+ if (!targetPath)
50
+ bail("Could not determine the config file path.");
51
+ // 2. Pick servers — built-in public servers plus any locally-registered
52
+ // private servers discovered under ~/.mpurdon-mcp/servers.d/.
53
+ const servers = allServers(BUILTIN_SERVERS);
54
+ const chosen = ensure(await p.multiselect({
55
+ message: "Which servers do you want to enable?",
56
+ required: true,
57
+ options: servers.map((s) => ({
58
+ value: s.key,
59
+ label: s.source === "local" ? `${s.title} (private)` : s.title,
60
+ hint: s.description,
61
+ })),
62
+ }));
63
+ // 3. Collect env per server.
64
+ const desired = {};
65
+ const notes = [];
66
+ for (const key of chosen) {
67
+ const server = servers.find((s) => s.key === key);
68
+ const env = {};
69
+ for (const v of server.env) {
70
+ const value = v.secret
71
+ ? ensure(await p.password({
72
+ message: `${server.title} — ${v.label}${v.required ? "" : " (optional)"}`,
73
+ validate: (val) => (v.required && !val ? "Required." : undefined),
74
+ }))
75
+ : ensure(await p.text({
76
+ message: `${server.title} — ${v.label}${v.required ? "" : " (optional)"}`,
77
+ placeholder: v.default ?? "",
78
+ defaultValue: v.default ?? "",
79
+ validate: (val) => (v.required && !val ? "Required." : undefined),
80
+ }));
81
+ if (value)
82
+ env[v.key] = value;
83
+ }
84
+ desired[key] = entryFor(server, env);
85
+ if (server.configFile)
86
+ notes.push(`[${server.key}] ${server.configFile.note}`);
87
+ if (server.setupNote)
88
+ notes.push(`[${server.key}] ${server.setupNote}`);
89
+ }
90
+ // 4. Plan and show the diff.
91
+ const config = readConfig(targetPath);
92
+ const changes = planChanges(config, desired);
93
+ p.note(renderPlan(targetPath, changes), "Planned changes");
94
+ const overwrites = changes.filter((c) => c.action === "update");
95
+ if (overwrites.length > 0) {
96
+ p.log.warn(`This will overwrite existing entries: ${overwrites.map((c) => c.key).join(", ")}`);
97
+ }
98
+ if (opts.dryRun) {
99
+ p.outro("Dry run — no files were modified.");
100
+ return;
101
+ }
102
+ const proceed = ensure(await p.confirm({ message: `Write these changes to ${targetPath}?` }));
103
+ if (!proceed)
104
+ bail("No changes written.");
105
+ // 5. Apply.
106
+ applyChanges(config, desired);
107
+ writeConfig(targetPath, config);
108
+ p.log.success(`Updated ${targetPath}`);
109
+ if (notes.length > 0) {
110
+ p.note(notes.join("\n\n"), "Follow-up steps");
111
+ }
112
+ p.outro(`Done. Restart ${host.title} so it picks up the new server${chosen.length > 1 ? "s" : ""}.`);
113
+ }
114
+ //# sourceMappingURL=configure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ /**
5
+ * Directory where private/local MCP servers drop a descriptor so the public
6
+ * configure CLI can offer them alongside the built-in public servers. Each
7
+ * file is `<key>.json` describing one server. This keeps proprietary details
8
+ * entirely on the user's machine — the public CLI ships no knowledge of them.
9
+ */
10
+ export const SERVERS_DIR = join(homedir(), ".mpurdon-mcp", "servers.d");
11
+ function isValidDescriptor(d) {
12
+ if (!d || typeof d !== "object")
13
+ return false;
14
+ const o = d;
15
+ return (typeof o.key === "string" &&
16
+ typeof o.title === "string" &&
17
+ typeof o.description === "string" &&
18
+ !!o.launch &&
19
+ typeof o.launch === "object" &&
20
+ typeof o.launch.command === "string" &&
21
+ Array.isArray(o.launch.args));
22
+ }
23
+ /**
24
+ * Read all locally-registered private server descriptors. Returns ServerDefs
25
+ * tagged `source: "local"`. Invalid/unreadable files are skipped with a warning
26
+ * to stderr rather than aborting the whole run.
27
+ */
28
+ export function discoverLocalServers() {
29
+ if (!existsSync(SERVERS_DIR))
30
+ return [];
31
+ const out = [];
32
+ for (const file of readdirSync(SERVERS_DIR)) {
33
+ if (!file.endsWith(".json"))
34
+ continue;
35
+ const path = join(SERVERS_DIR, file);
36
+ try {
37
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
38
+ if (!isValidDescriptor(parsed)) {
39
+ process.stderr.write(`[mcp-servers] skipping invalid descriptor: ${path}\n`);
40
+ continue;
41
+ }
42
+ out.push({
43
+ key: parsed.key,
44
+ title: parsed.title,
45
+ description: parsed.description,
46
+ launch: parsed.launch,
47
+ env: parsed.env ?? [],
48
+ configFile: parsed.configFile,
49
+ setupNote: parsed.setupNote,
50
+ source: "local",
51
+ });
52
+ }
53
+ catch (err) {
54
+ process.stderr.write(`[mcp-servers] could not read descriptor ${path}: ${err.message}\n`);
55
+ }
56
+ }
57
+ return out;
58
+ }
59
+ /**
60
+ * Merge built-in public servers with discovered local ones. Local descriptors
61
+ * win on key collision (lets you override a public server with a local build).
62
+ */
63
+ export function allServers(builtin) {
64
+ const local = discoverLocalServers();
65
+ const byKey = new Map();
66
+ for (const s of builtin)
67
+ byKey.set(s.key, s);
68
+ for (const s of local)
69
+ byKey.set(s.key, s);
70
+ return [...byKey.values()];
71
+ }
72
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
package/dist/hosts.js ADDED
@@ -0,0 +1,46 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir, platform } from "node:os";
3
+ import { join } from "node:path";
4
+ function desktopConfigPath() {
5
+ const home = homedir();
6
+ switch (platform()) {
7
+ case "darwin":
8
+ return join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
9
+ case "win32":
10
+ return join(process.env.APPDATA ?? join(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
11
+ default:
12
+ // Linux / others
13
+ return join(home, ".config", "Claude", "claude_desktop_config.json");
14
+ }
15
+ }
16
+ export const HOSTS = [
17
+ {
18
+ id: "desktop",
19
+ title: "Claude Desktop",
20
+ resolvePath: () => desktopConfigPath(),
21
+ detect: () => {
22
+ const p = desktopConfigPath();
23
+ // The parent dir existing is a good signal even before any MCP is added.
24
+ return existsSync(p) || existsSync(join(p, ".."));
25
+ },
26
+ },
27
+ {
28
+ id: "code",
29
+ title: "Claude Code",
30
+ resolvePath: () => join(homedir(), ".claude.json"),
31
+ detect: () => existsSync(join(homedir(), ".claude.json")),
32
+ },
33
+ {
34
+ id: "cowork",
35
+ title: "Claude Cowork (workspace .mcp.json)",
36
+ resolvePath: ({ workspaceDir }) => workspaceDir ? join(workspaceDir, ".mcp.json") : null,
37
+ detect: () => true, // always selectable; path is prompted
38
+ },
39
+ ];
40
+ export function getHost(id) {
41
+ const host = HOSTS.find((h) => h.id === id);
42
+ if (!host)
43
+ throw new Error(`Unknown host: ${id}`);
44
+ return host;
45
+ }
46
+ //# sourceMappingURL=hosts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosts.js","sourceRoot":"","sources":["../src/hosts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAgBjC,SAAS,iBAAiB;IACxB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,QAAQ,QAAQ,EAAE,EAAE,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,IAAI,CACT,IAAI,EACJ,SAAS,EACT,qBAAqB,EACrB,QAAQ,EACR,4BAA4B,CAC7B,CAAC;QACJ,KAAK,OAAO;YACV,OAAO,IAAI,CACT,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EACvD,QAAQ,EACR,4BAA4B,CAC7B,CAAC;QACJ;YACE,iBAAiB;YACjB,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAc;IAC9B;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,GAAG,EAAE,CAAC,iBAAiB,EAAE;QACtC,MAAM,EAAE,GAAG,EAAE;YACX,MAAM,CAAC,GAAG,iBAAiB,EAAE,CAAC;YAC9B,yEAAyE;YACzE,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;KACF;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC;QAClD,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;KAC1D;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,qCAAqC;QAC5C,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,CAChC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI;QACvD,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,sCAAsC;KAC3D;CACF,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { configure } from "./configure.js";
3
+ import { BUILTIN_SERVERS } from "./registry.js";
4
+ import { allServers } from "./discovery.js";
5
+ const HELP = `@mpurdon/mcp-servers — install & configure @mpurdon MCP servers
6
+
7
+ Usage:
8
+ npx @mpurdon/mcp-servers configure [--dry-run]
9
+ npx @mpurdon/mcp-servers list
10
+ npx @mpurdon/mcp-servers --help
11
+
12
+ Commands:
13
+ configure Interactively register servers into Claude Desktop, Code, or Cowork.
14
+ list Print the available servers.
15
+
16
+ Options:
17
+ --dry-run Show the planned config changes without writing anything.
18
+ -h, --help Show this help.
19
+ `;
20
+ function list() {
21
+ for (const s of allServers(BUILTIN_SERVERS)) {
22
+ const origin = s.source === "local" ? "(private, local)" : (s.packageName ?? "");
23
+ process.stdout.write(`${s.key.padEnd(12)} ${origin}\n`);
24
+ process.stdout.write(`${" ".repeat(12)} ${s.description}\n`);
25
+ }
26
+ }
27
+ async function main() {
28
+ const argv = process.argv.slice(2);
29
+ if (argv.includes("-h") || argv.includes("--help") || argv.length === 0) {
30
+ process.stdout.write(HELP);
31
+ return;
32
+ }
33
+ const command = argv[0];
34
+ switch (command) {
35
+ case "configure":
36
+ await configure({ dryRun: argv.includes("--dry-run") });
37
+ break;
38
+ case "list":
39
+ list();
40
+ break;
41
+ default:
42
+ process.stderr.write(`Unknown command: ${command}\n\n${HELP}`);
43
+ process.exit(1);
44
+ }
45
+ }
46
+ main().catch((err) => {
47
+ process.stderr.write(`[mcp-servers] fatal: ${err.stack ?? err}\n`);
48
+ process.exit(1);
49
+ });
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,IAAI,GAAG;;;;;;;;;;;;;;CAcZ,CAAC;AAEF,SAAS,IAAI;IACX,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QAC5C,MAAM,MAAM,GACV,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC;QACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,WAAW;YACd,MAAM,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YACxD,MAAM;QACR,KAAK,MAAM;YACT,IAAI,EAAE,CAAC;YACP,MAAM;QACR;YACE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAyB,GAAa,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;IAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The single source of truth for which @mpurdon MCP servers exist and how they
3
+ * are configured. The configurator drives every prompt from this list.
4
+ */
5
+ export const SERVERS = [
6
+ {
7
+ key: "mongodb",
8
+ packageName: "@mpurdon/mcp-mongodb",
9
+ title: "MongoDB",
10
+ description: "Query/manage MongoDB across dev/stg/prd with production write-protection.",
11
+ env: [],
12
+ configFile: {
13
+ relativePath: ".mongodb-mcp/config.json",
14
+ note: "Create ~/.mongodb-mcp/config.json with your dev/stg/prd connection strings " +
15
+ "(chmod 600). See the @mpurdon/mcp-mongodb README for the exact shape.",
16
+ },
17
+ },
18
+ {
19
+ key: "freshbooks",
20
+ packageName: "@mpurdon/mcp-freshbooks",
21
+ title: "FreshBooks",
22
+ description: "FreshBooks invoices, clients, items, and time entries.",
23
+ env: [
24
+ {
25
+ key: "FRESHBOOKS_CLIENT_ID",
26
+ label: "FreshBooks app Client ID",
27
+ required: true,
28
+ secret: false,
29
+ },
30
+ {
31
+ key: "FRESHBOOKS_CLIENT_SECRET",
32
+ label: "FreshBooks app Client Secret",
33
+ required: true,
34
+ secret: true,
35
+ },
36
+ ],
37
+ setupNote: "After registering, run `npx -y @mpurdon/mcp-freshbooks freshbooks-setup` once " +
38
+ "to complete the OAuth flow and store tokens at ~/.freshbooks-mcp/tokens.json.",
39
+ },
40
+ {
41
+ key: "sumologic",
42
+ packageName: "@mpurdon/mcp-sumologic",
43
+ title: "Sumo Logic",
44
+ description: "Run Sumo Logic searches and inspect results.",
45
+ env: [
46
+ {
47
+ key: "SUMOLOGIC_ACCESS_ID",
48
+ label: "Sumo Logic Access ID",
49
+ required: true,
50
+ secret: false,
51
+ },
52
+ {
53
+ key: "SUMOLOGIC_ACCESS_KEY",
54
+ label: "Sumo Logic Access Key",
55
+ required: true,
56
+ secret: true,
57
+ },
58
+ {
59
+ key: "SUMOLOGIC_API_ENDPOINT",
60
+ label: "Sumo Logic API endpoint",
61
+ required: false,
62
+ secret: false,
63
+ default: "https://api.sumologic.com/api",
64
+ },
65
+ ],
66
+ },
67
+ {
68
+ key: "github",
69
+ packageName: "@mpurdon/mcp-github",
70
+ title: "GitHub",
71
+ description: "GitHub org/repo/PR/issue and Actions workflow operations.",
72
+ env: [
73
+ {
74
+ key: "GITHUB_TOKEN",
75
+ label: "GitHub personal access token",
76
+ required: true,
77
+ secret: true,
78
+ },
79
+ ],
80
+ },
81
+ ];
82
+ /** Built-in (public, npm-published) servers, tagged with their source. */
83
+ export const BUILTIN_SERVERS = SERVERS.map((s) => ({
84
+ ...s,
85
+ source: "builtin",
86
+ }));
87
+ export function findServer(key) {
88
+ return SERVERS.find((s) => s.key === key);
89
+ }
90
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+CH,MAAM,CAAC,MAAM,OAAO,GAAgB;IAClC;QACE,GAAG,EAAE,SAAS;QACd,WAAW,EAAE,sBAAsB;QACnC,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,2EAA2E;QAC7E,GAAG,EAAE,EAAE;QACP,UAAU,EAAE;YACV,YAAY,EAAE,0BAA0B;YACxC,IAAI,EACF,6EAA6E;gBAC7E,uEAAuE;SAC1E;KACF;IACD;QACE,GAAG,EAAE,YAAY;QACjB,WAAW,EAAE,yBAAyB;QACtC,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,wDAAwD;QACrE,GAAG,EAAE;YACH;gBACE,GAAG,EAAE,sBAAsB;gBAC3B,KAAK,EAAE,0BAA0B;gBACjC,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,KAAK;aACd;YACD;gBACE,GAAG,EAAE,0BAA0B;gBAC/B,KAAK,EAAE,8BAA8B;gBACrC,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;aACb;SACF;QACD,SAAS,EACP,gFAAgF;YAChF,+EAA+E;KAClF;IACD;QACE,GAAG,EAAE,WAAW;QAChB,WAAW,EAAE,wBAAwB;QACrC,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,8CAA8C;QAC3D,GAAG,EAAE;YACH;gBACE,GAAG,EAAE,qBAAqB;gBAC1B,KAAK,EAAE,sBAAsB;gBAC7B,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,KAAK;aACd;YACD;gBACE,GAAG,EAAE,sBAAsB;gBAC3B,KAAK,EAAE,uBAAuB;gBAC9B,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;aACb;YACD;gBACE,GAAG,EAAE,wBAAwB;gBAC7B,KAAK,EAAE,yBAAyB;gBAChC,QAAQ,EAAE,KAAK;gBACf,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,+BAA+B;aACzC;SACF;KACF;IACD;QACE,GAAG,EAAE,QAAQ;QACb,WAAW,EAAE,qBAAqB;QAClC,KAAK,EAAE,QAAQ;QACf,WAAW,EAAE,2DAA2D;QACxE,GAAG,EAAE;YACH;gBACE,GAAG,EAAE,cAAc;gBACnB,KAAK,EAAE,8BAA8B;gBACrC,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,IAAI;aACb;SACF;KACF;CACF,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,eAAe,GAAgB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC;IACJ,MAAM,EAAE,SAAS;CAClB,CAAC,CAAC,CAAC;AAEJ,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAC5C,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@mpurdon/mcp-servers",
3
+ "version": "0.1.0",
4
+ "description": "Installer/configurator for @mpurdon MCP servers across Claude Desktop, Cowork, and Code.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mcp-servers": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "claude",
20
+ "cli",
21
+ "installer"
22
+ ],
23
+ "license": "MIT",
24
+ "author": "Matthew Purdon",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/mpurdon/mcp-servers.git",
28
+ "directory": "packages/cli"
29
+ },
30
+ "dependencies": {
31
+ "@clack/prompts": "^0.8.2"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.10.2",
35
+ "eslint": "^9.17.0",
36
+ "tsx": "^4.19.2",
37
+ "typescript": "^5.7.2",
38
+ "@mpurdon/eslint-config": "0.0.0",
39
+ "@mpurdon/tsconfig": "0.0.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=20"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "dev": "tsx src/index.ts",
47
+ "start": "node dist/index.js",
48
+ "typecheck": "tsc --noEmit",
49
+ "lint": "eslint ."
50
+ }
51
+ }