@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 +21 -0
- package/README.md +57 -0
- package/dist/config-file.js +59 -0
- package/dist/config-file.js.map +1 -0
- package/dist/configure.js +114 -0
- package/dist/configure.js.map +1 -0
- package/dist/discovery.js +72 -0
- package/dist/discovery.js.map +1 -0
- package/dist/hosts.js +46 -0
- package/dist/hosts.js.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.js +90 -0
- package/dist/registry.js.map +1 -0
- package/package.json +51 -0
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"}
|
package/dist/registry.js
ADDED
|
@@ -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
|
+
}
|