@macula-io/mcp 0.3.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 +201 -0
- package/README.md +192 -0
- package/dist/bin/install.d.ts +2 -0
- package/dist/bin/install.js +146 -0
- package/dist/bin/install.js.map +1 -0
- package/dist/bin/status.d.ts +2 -0
- package/dist/bin/status.js +86 -0
- package/dist/bin/status.js.map +1 -0
- package/dist/bin/uninstall.d.ts +2 -0
- package/dist/bin/uninstall.js +81 -0
- package/dist/bin/uninstall.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/install/config_merge.d.ts +24 -0
- package/dist/install/config_merge.js +141 -0
- package/dist/install/config_merge.js.map +1 -0
- package/dist/install/existing_cli.d.ts +6 -0
- package/dist/install/existing_cli.js +24 -0
- package/dist/install/existing_cli.js.map +1 -0
- package/dist/install/mcp_clients/claude_code.d.ts +7 -0
- package/dist/install/mcp_clients/claude_code.js +51 -0
- package/dist/install/mcp_clients/claude_code.js.map +1 -0
- package/dist/install/mcp_clients/claude_desktop.d.ts +7 -0
- package/dist/install/mcp_clients/claude_desktop.js +40 -0
- package/dist/install/mcp_clients/claude_desktop.js.map +1 -0
- package/dist/install/mcp_clients/cursor.d.ts +7 -0
- package/dist/install/mcp_clients/cursor.js +30 -0
- package/dist/install/mcp_clients/cursor.js.map +1 -0
- package/dist/install/mcp_clients/index.d.ts +11 -0
- package/dist/install/mcp_clients/index.js +12 -0
- package/dist/install/mcp_clients/index.js.map +1 -0
- package/dist/install/mcp_clients/windsurf.d.ts +7 -0
- package/dist/install/mcp_clients/windsurf.js +30 -0
- package/dist/install/mcp_clients/windsurf.js.map +1 -0
- package/dist/install/platform.d.ts +9 -0
- package/dist/install/platform.js +34 -0
- package/dist/install/platform.js.map +1 -0
- package/dist/macula_cli.d.ts +88 -0
- package/dist/macula_cli.js +189 -0
- package/dist/macula_cli.js.map +1 -0
- package/dist/mesh_artifact.d.ts +2 -0
- package/dist/mesh_artifact.js +63 -0
- package/dist/mesh_artifact.js.map +1 -0
- package/dist/mesh_call.d.ts +2 -0
- package/dist/mesh_call.js +45 -0
- package/dist/mesh_call.js.map +1 -0
- package/dist/mesh_identity.d.ts +2 -0
- package/dist/mesh_identity.js +28 -0
- package/dist/mesh_identity.js.map +1 -0
- package/dist/mesh_publish.d.ts +2 -0
- package/dist/mesh_publish.js +33 -0
- package/dist/mesh_publish.js.map +1 -0
- package/dist/mesh_watch.d.ts +2 -0
- package/dist/mesh_watch.js +53 -0
- package/dist/mesh_watch.js.map +1 -0
- package/dist/reply.d.ts +16 -0
- package/dist/reply.js +24 -0
- package/dist/reply.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// macula-mcp uninstall — remove the `macula` entry from every
|
|
3
|
+
// detected MCP client's config. Idempotent.
|
|
4
|
+
//
|
|
5
|
+
// Does NOT remove the macula-cli binary or its persisted identity
|
|
6
|
+
// (see macula-cli's own uninstall.sh/uninstall.ps1 for that). The
|
|
7
|
+
// MCP integration is the only thing this command touches.
|
|
8
|
+
import { ALL, detected } from "../install/mcp_clients/index.js";
|
|
9
|
+
const VERSION = "0.3.0";
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
const a = { all: false, help: false };
|
|
12
|
+
for (const arg of argv) {
|
|
13
|
+
if (arg === "--help" || arg === "-h")
|
|
14
|
+
a.help = true;
|
|
15
|
+
else if (arg === "--all")
|
|
16
|
+
a.all = true;
|
|
17
|
+
else {
|
|
18
|
+
console.error(`[macula-mcp uninstall] unknown flag: ${arg}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
}
|
|
24
|
+
function help() {
|
|
25
|
+
console.log(`macula-mcp uninstall ${VERSION}
|
|
26
|
+
|
|
27
|
+
Usage: npx @macula-io/mcp uninstall [--all]
|
|
28
|
+
|
|
29
|
+
Removes the 'macula' MCP server entry from every detected MCP
|
|
30
|
+
client's config. The macula-cli binary and your persisted identity
|
|
31
|
+
are NOT touched.
|
|
32
|
+
|
|
33
|
+
Flags:
|
|
34
|
+
--all Touch every supported client config, not just those whose
|
|
35
|
+
install directories are present. Use this if you uninstalled
|
|
36
|
+
an editor but want to clean up its config file.
|
|
37
|
+
--help, -h
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
function info(msg) {
|
|
41
|
+
console.log(`[macula-mcp uninstall] ${msg}`);
|
|
42
|
+
}
|
|
43
|
+
function ok(msg) {
|
|
44
|
+
console.log(`[macula-mcp uninstall] ✓ ${msg}`);
|
|
45
|
+
}
|
|
46
|
+
function err(msg) {
|
|
47
|
+
console.error(`[macula-mcp uninstall] ✗ ${msg}`);
|
|
48
|
+
}
|
|
49
|
+
async function main() {
|
|
50
|
+
const args = parseArgs(process.argv.slice(2));
|
|
51
|
+
if (args.help) {
|
|
52
|
+
help();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const targets = args.all ? ALL : detected();
|
|
56
|
+
if (targets.length === 0) {
|
|
57
|
+
ok("no MCP clients detected; nothing to do.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
info(`targets: ${targets.map((c) => c.CLIENT_LABEL).join(", ")}`);
|
|
61
|
+
for (const client of targets) {
|
|
62
|
+
await uninstallOne(client);
|
|
63
|
+
}
|
|
64
|
+
ok("done.");
|
|
65
|
+
}
|
|
66
|
+
async function uninstallOne(c) {
|
|
67
|
+
try {
|
|
68
|
+
const result = await c.uninstall();
|
|
69
|
+
info(`${c.CLIENT_LABEL}: ${result.message} (${result.configPath})`);
|
|
70
|
+
if (result.backupPath)
|
|
71
|
+
info(` backup: ${result.backupPath}`);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
err(`${c.CLIENT_LABEL}: ${e instanceof Error ? e.message : String(e)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
main().catch((e) => {
|
|
78
|
+
err(`fatal: ${e instanceof Error ? e.stack ?? e.message : String(e)}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/bin/uninstall.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// macula-mcp — a Model Context Protocol server that exposes the Macula mesh to
|
|
3
|
+
// any agent harness (Claude Code, Cursor, Cline, Continue, ...).
|
|
4
|
+
//
|
|
5
|
+
// Topology: thin client. macula-mcp speaks MCP over stdio to the agent, and
|
|
6
|
+
// shells out to macula-cli (macula-io/macula-cli), a one-shot scriptable CLI
|
|
7
|
+
// built directly on macula-go-sdk. macula-mcp owns no mesh logic of its
|
|
8
|
+
// own -- macula-cli does the QUIC handshake/call/publish/watch/content
|
|
9
|
+
// transfer as a subprocess per tool call.
|
|
10
|
+
//
|
|
11
|
+
// agent harness --MCP/stdio--> macula-mcp --spawns, parses stdout--> macula-cli --QUIC--> mesh
|
|
12
|
+
//
|
|
13
|
+
// Reworked 2026-08-29 from a hecate-daemon-backed design (HTTP over a
|
|
14
|
+
// local Unix socket) to this one: hecate-daemon is a leftover of an
|
|
15
|
+
// abandoned local browser/UI plan and is no longer something this server
|
|
16
|
+
// should depend on. This is a deliberately LEAN rework, not a like-for-
|
|
17
|
+
// like swap -- macula-cli is a one-shot process with no daemon and no
|
|
18
|
+
// storage, so standing subscriptions, an activity/inbox audit log, and
|
|
19
|
+
// peer listing don't carry over; mesh_watch (bounded, synchronous)
|
|
20
|
+
// replaces the old subscribe/unsubscribe/subscriptions/inbox quartet.
|
|
21
|
+
// See macula-io/macula-cli's own project memory for the fuller tradeoff.
|
|
22
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
24
|
+
import { registerIdentity } from "./mesh_identity.js";
|
|
25
|
+
import { registerMeshCall } from "./mesh_call.js";
|
|
26
|
+
import { registerMeshArtifact } from "./mesh_artifact.js";
|
|
27
|
+
import { registerMeshPublish } from "./mesh_publish.js";
|
|
28
|
+
import { registerMeshWatch } from "./mesh_watch.js";
|
|
29
|
+
const server = new McpServer({
|
|
30
|
+
name: "macula-mcp",
|
|
31
|
+
version: "0.3.0",
|
|
32
|
+
});
|
|
33
|
+
// Resources — read-only context an agent should consult before acting.
|
|
34
|
+
registerIdentity(server);
|
|
35
|
+
// Tools — actions, each a one-shot macula-cli subprocess call.
|
|
36
|
+
registerMeshCall(server);
|
|
37
|
+
registerMeshArtifact(server);
|
|
38
|
+
registerMeshPublish(server);
|
|
39
|
+
registerMeshWatch(server);
|
|
40
|
+
async function main() {
|
|
41
|
+
const transport = new StdioServerTransport();
|
|
42
|
+
await server.connect(transport);
|
|
43
|
+
// stderr is safe; stdout is the MCP channel.
|
|
44
|
+
console.error("macula-mcp ready (stdio)");
|
|
45
|
+
}
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.error("macula-mcp fatal:", err);
|
|
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,+EAA+E;AAC/E,iEAAiE;AACjE,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,wEAAwE;AACxE,uEAAuE;AACvE,0CAA0C;AAC1C,EAAE;AACF,uGAAuG;AACvG,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,yEAAyE;AACzE,wEAAwE;AACxE,sEAAsE;AACtE,uEAAuE;AACvE,mEAAmE;AACnE,sEAAsE;AACtE,yEAAyE;AAEzE,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,uEAAuE;AACvE,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAEzB,+DAA+D;AAC/D,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE1B,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,6CAA6C;IAC7C,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAC5C,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;IACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface McpServerEntry {
|
|
2
|
+
command: string;
|
|
3
|
+
args?: string[];
|
|
4
|
+
env?: Record<string, string>;
|
|
5
|
+
}
|
|
6
|
+
export type MergeOutcome = "added" | "replaced" | "unchanged" | "skipped_conflict";
|
|
7
|
+
export interface MergeResult {
|
|
8
|
+
outcome: MergeOutcome;
|
|
9
|
+
configPath: string;
|
|
10
|
+
backupPath?: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Merge a single mcpServers entry into the config at `path`, under
|
|
15
|
+
* the top-level key `mcpServers` (the standard for Claude Code /
|
|
16
|
+
* Cursor / Windsurf). Idempotent + safe-merge.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mergeMcpServer(path: string, name: string, entry: McpServerEntry, opts?: {
|
|
19
|
+
force?: boolean;
|
|
20
|
+
}): Promise<MergeResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Remove a single mcpServers entry. Idempotent.
|
|
23
|
+
*/
|
|
24
|
+
export declare function removeMcpServer(path: string, name: string): Promise<MergeResult>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Safe JSON-config merge for MCP-client configs.
|
|
2
|
+
//
|
|
3
|
+
// MCP clients store config as JSON files with a top-level shape like
|
|
4
|
+
// `{ "mcpServers": { "<name>": { command, args, env? } } }` (Claude
|
|
5
|
+
// Code / Cursor / Windsurf style) or a vendor-specific variant. This
|
|
6
|
+
// utility:
|
|
7
|
+
//
|
|
8
|
+
// * preserves existing entries (never overwrites unrelated servers)
|
|
9
|
+
// * writes a timestamped backup before any change
|
|
10
|
+
// * is idempotent: a re-run with the same entry is a no-op
|
|
11
|
+
// * never produces malformed JSON: parse-fail aborts with a clear
|
|
12
|
+
// remediation path
|
|
13
|
+
//
|
|
14
|
+
// The actual per-client write happens in `mcp_clients/<name>.ts`; this
|
|
15
|
+
// module owns the JSON-handling primitives they all share.
|
|
16
|
+
import { readFile, writeFile, mkdir, stat, copyFile } from "node:fs/promises";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
import { dirname } from "node:path";
|
|
19
|
+
/**
|
|
20
|
+
* Read the JSON config at `path`. If absent, returns an empty object.
|
|
21
|
+
* If present but malformed, throws — never returns a partially-parsed
|
|
22
|
+
* value (callers can decide whether to abort or copy-aside and
|
|
23
|
+
* re-create).
|
|
24
|
+
*/
|
|
25
|
+
async function readConfig(path) {
|
|
26
|
+
if (!existsSync(path))
|
|
27
|
+
return {};
|
|
28
|
+
const raw = await readFile(path, "utf8");
|
|
29
|
+
if (raw.trim().length === 0)
|
|
30
|
+
return {};
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
throw new Error(`existing config at ${path} is not valid JSON. ` +
|
|
36
|
+
`Refusing to overwrite. Fix it manually or remove the file and re-run. ` +
|
|
37
|
+
`(parser said: ${e instanceof Error ? e.message : String(e)})`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Write a backup copy alongside the original, named with a timestamp.
|
|
42
|
+
* Skipped if the file doesn't exist (nothing to back up).
|
|
43
|
+
*/
|
|
44
|
+
async function backup(path) {
|
|
45
|
+
if (!existsSync(path))
|
|
46
|
+
return undefined;
|
|
47
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
48
|
+
const bak = `${path}.macula-bak-${ts}`;
|
|
49
|
+
await copyFile(path, bak);
|
|
50
|
+
return bak;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Merge a single mcpServers entry into the config at `path`, under
|
|
54
|
+
* the top-level key `mcpServers` (the standard for Claude Code /
|
|
55
|
+
* Cursor / Windsurf). Idempotent + safe-merge.
|
|
56
|
+
*/
|
|
57
|
+
export async function mergeMcpServer(path, name, entry, opts = {}) {
|
|
58
|
+
const existing = await readConfig(path);
|
|
59
|
+
const servers = existing.mcpServers ?? {};
|
|
60
|
+
const current = servers[name];
|
|
61
|
+
if (current && deepEqual(current, entry)) {
|
|
62
|
+
return {
|
|
63
|
+
outcome: "unchanged",
|
|
64
|
+
configPath: path,
|
|
65
|
+
message: `entry '${name}' already present and matches; no-op`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (current && !opts.force) {
|
|
69
|
+
return {
|
|
70
|
+
outcome: "skipped_conflict",
|
|
71
|
+
configPath: path,
|
|
72
|
+
message: `entry '${name}' already present with different settings. ` +
|
|
73
|
+
`Re-run with --force to replace, or remove the entry manually.`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const next = {
|
|
77
|
+
...existing,
|
|
78
|
+
mcpServers: { ...servers, [name]: entry },
|
|
79
|
+
};
|
|
80
|
+
const bak = await backup(path);
|
|
81
|
+
await mkdir(dirname(path), { recursive: true });
|
|
82
|
+
await writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
83
|
+
return {
|
|
84
|
+
outcome: current ? "replaced" : "added",
|
|
85
|
+
configPath: path,
|
|
86
|
+
backupPath: bak,
|
|
87
|
+
message: current ? "replaced existing entry" : "added entry",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Remove a single mcpServers entry. Idempotent.
|
|
92
|
+
*/
|
|
93
|
+
export async function removeMcpServer(path, name) {
|
|
94
|
+
if (!existsSync(path)) {
|
|
95
|
+
return {
|
|
96
|
+
outcome: "unchanged",
|
|
97
|
+
configPath: path,
|
|
98
|
+
message: "config does not exist; nothing to remove",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const existing = await readConfig(path);
|
|
102
|
+
const servers = existing.mcpServers ?? {};
|
|
103
|
+
if (!(name in servers)) {
|
|
104
|
+
return {
|
|
105
|
+
outcome: "unchanged",
|
|
106
|
+
configPath: path,
|
|
107
|
+
message: `entry '${name}' not present; no-op`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const { [name]: _removed, ...rest } = servers;
|
|
111
|
+
const next = { ...existing, mcpServers: rest };
|
|
112
|
+
const bak = await backup(path);
|
|
113
|
+
await writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
114
|
+
return {
|
|
115
|
+
outcome: "replaced",
|
|
116
|
+
configPath: path,
|
|
117
|
+
backupPath: bak,
|
|
118
|
+
message: `removed entry '${name}'`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function deepEqual(a, b) {
|
|
122
|
+
if (a === b)
|
|
123
|
+
return true;
|
|
124
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
|
|
125
|
+
return false;
|
|
126
|
+
const ka = Object.keys(a).sort();
|
|
127
|
+
const kb = Object.keys(b).sort();
|
|
128
|
+
if (ka.length !== kb.length)
|
|
129
|
+
return false;
|
|
130
|
+
for (let i = 0; i < ka.length; i++)
|
|
131
|
+
if (ka[i] !== kb[i])
|
|
132
|
+
return false;
|
|
133
|
+
for (const k of ka) {
|
|
134
|
+
if (!deepEqual(a[k], b[k]))
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
// Silence unused-warning for object-destructure rest pattern above.
|
|
140
|
+
void stat;
|
|
141
|
+
//# sourceMappingURL=config_merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config_merge.js","sourceRoot":"","sources":["../../src/install/config_merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Probe for an installed macula-cli binary.
|
|
2
|
+
//
|
|
3
|
+
// The installer's primary check: macula-mcp shells out to macula-cli for
|
|
4
|
+
// every mesh operation (src/macula_cli.ts), so there's nothing to
|
|
5
|
+
// register in an MCP client's config that will actually work without it
|
|
6
|
+
// on PATH first. `macula-cli identity` is purely local -- no network, no
|
|
7
|
+
// station -- so it's a clean "is this binary present and functional"
|
|
8
|
+
// probe, not a mesh connectivity check.
|
|
9
|
+
import { identity, MaculaCliUnavailable } from "../macula_cli.js";
|
|
10
|
+
export async function probe() {
|
|
11
|
+
try {
|
|
12
|
+
const id = await identity();
|
|
13
|
+
return { available: true, nodeId: id.node_id };
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
const reason = e instanceof MaculaCliUnavailable
|
|
17
|
+
? e.message
|
|
18
|
+
: e instanceof Error
|
|
19
|
+
? e.message
|
|
20
|
+
: String(e);
|
|
21
|
+
return { available: false, reason };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=existing_cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"existing_cli.js","sourceRoot":"","sources":["../../src/install/existing_cli.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,yEAAyE;AACzE,kEAAkE;AAClE,wEAAwE;AACxE,yEAAyE;AACzE,qEAAqE;AACrE,wCAAwC;AAExC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAQlE,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;IACjD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,MAAM,GACV,CAAC,YAAY,oBAAoB;YAC/B,CAAC,CAAC,CAAC,CAAC,OAAO;YACX,CAAC,CAAC,CAAC,YAAY,KAAK;gBAClB,CAAC,CAAC,CAAC,CAAC,OAAO;gBACX,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAClB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MergeResult } from "../config_merge.js";
|
|
2
|
+
export declare const CLIENT_ID = "claude-code";
|
|
3
|
+
export declare const CLIENT_LABEL = "Claude Code";
|
|
4
|
+
export declare function configPath(): string;
|
|
5
|
+
export declare function isInstalled(): boolean;
|
|
6
|
+
export declare function install(): Promise<MergeResult>;
|
|
7
|
+
export declare function uninstall(): Promise<MergeResult>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Claude Code MCP client integration.
|
|
2
|
+
//
|
|
3
|
+
// Config path: ~/.claude/mcp.json (global) AND/OR project-local
|
|
4
|
+
// .mcp.json. MVP writes the global file only; project-local is the
|
|
5
|
+
// user's choice on a per-repo basis.
|
|
6
|
+
//
|
|
7
|
+
// Entry format (Claude Code's spec):
|
|
8
|
+
// {
|
|
9
|
+
// "mcpServers": {
|
|
10
|
+
// "macula": {
|
|
11
|
+
// "command": "npx",
|
|
12
|
+
// "args": ["-y", "-p", "@macula-io/mcp", "macula-mcp"]
|
|
13
|
+
// }
|
|
14
|
+
// }
|
|
15
|
+
// }
|
|
16
|
+
//
|
|
17
|
+
// The extra "-p @macula-io/mcp macula-mcp" (rather than bare "npx -y
|
|
18
|
+
// @macula-io/mcp") is load-bearing, not decoration: this package
|
|
19
|
+
// publishes FOUR bin entries (macula-mcp, macula-mcp-install,
|
|
20
|
+
// macula-mcp-uninstall, macula-mcp-status), none of which is literally
|
|
21
|
+
// "mcp" -- npx's default "run the bin matching the package's own short
|
|
22
|
+
// name" heuristic has nothing to match and fails outright with "could
|
|
23
|
+
// not determine executable to run". Confirmed live packing a real
|
|
24
|
+
// tarball and running bare `npx -y <tarball>` before finding this --
|
|
25
|
+
// `-p <pkg> <bin>` names the executable explicitly and works.
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import { existsSync } from "node:fs";
|
|
29
|
+
import { mergeMcpServer, removeMcpServer, } from "../config_merge.js";
|
|
30
|
+
export const CLIENT_ID = "claude-code";
|
|
31
|
+
export const CLIENT_LABEL = "Claude Code";
|
|
32
|
+
export function configPath() {
|
|
33
|
+
return join(homedir(), ".claude", "mcp.json");
|
|
34
|
+
}
|
|
35
|
+
export function isInstalled() {
|
|
36
|
+
// Claude Code installs at ~/.claude — presence of that directory
|
|
37
|
+
// (or any of the canonical files) is a strong-enough signal for
|
|
38
|
+
// MVP. False positives are harmless (we just write a config file
|
|
39
|
+
// the client can pick up later).
|
|
40
|
+
return existsSync(join(homedir(), ".claude"));
|
|
41
|
+
}
|
|
42
|
+
export async function install() {
|
|
43
|
+
return mergeMcpServer(configPath(), "macula", {
|
|
44
|
+
command: "npx",
|
|
45
|
+
args: ["-y", "-p", "@macula-io/mcp", "macula-mcp"],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export async function uninstall() {
|
|
49
|
+
return removeMcpServer(configPath(), "macula");
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=claude_code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude_code.js","sourceRoot":"","sources":["../../../src/install/mcp_clients/claude_code.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,gEAAgE;AAChE,mEAAmE;AACnE,qCAAqC;AACrC,EAAE;AACF,qCAAqC;AACrC,MAAM;AACN,sBAAsB;AACtB,oBAAoB;AACpB,4BAA4B;AAC5B,+DAA+D;AAC/D,UAAU;AACV,QAAQ;AACR,MAAM;AACN,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,8DAA8D;AAC9D,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,kEAAkE;AAClE,qEAAqE;AACrE,8DAA8D;AAE9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,cAAc,EACd,eAAe,GAEhB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,iEAAiE;IACjE,gEAAgE;IAChE,iEAAiE;IACjE,iCAAiC;IACjC,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,OAAO,cAAc,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE;QAC5C,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,OAAO,eAAe,CAAC,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MergeResult } from "../config_merge.js";
|
|
2
|
+
export declare const CLIENT_ID = "claude-desktop";
|
|
3
|
+
export declare const CLIENT_LABEL = "Claude Desktop";
|
|
4
|
+
export declare function configPath(): string;
|
|
5
|
+
export declare function isInstalled(): boolean;
|
|
6
|
+
export declare function install(): Promise<MergeResult>;
|
|
7
|
+
export declare function uninstall(): Promise<MergeResult>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Claude Desktop MCP client integration.
|
|
2
|
+
//
|
|
3
|
+
// Config paths vary by OS:
|
|
4
|
+
// macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
5
|
+
// Linux: ~/.config/Claude/claude_desktop_config.json
|
|
6
|
+
// Windows: %APPDATA%\Claude\claude_desktop_config.json
|
|
7
|
+
//
|
|
8
|
+
// Schema is the same JSON shape as Claude Code:
|
|
9
|
+
// { "mcpServers": { "macula": { "command": ..., "args": ... } } }
|
|
10
|
+
import { homedir, platform } from "node:os";
|
|
11
|
+
import { join, dirname } from "node:path";
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { mergeMcpServer, removeMcpServer, } from "../config_merge.js";
|
|
14
|
+
export const CLIENT_ID = "claude-desktop";
|
|
15
|
+
export const CLIENT_LABEL = "Claude Desktop";
|
|
16
|
+
export function configPath() {
|
|
17
|
+
const p = platform();
|
|
18
|
+
if (p === "darwin") {
|
|
19
|
+
return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
20
|
+
}
|
|
21
|
+
if (p === "win32") {
|
|
22
|
+
const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
|
|
23
|
+
return join(appData, "Claude", "claude_desktop_config.json");
|
|
24
|
+
}
|
|
25
|
+
// linux + everything else
|
|
26
|
+
return join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
27
|
+
}
|
|
28
|
+
export function isInstalled() {
|
|
29
|
+
return existsSync(dirname(configPath()));
|
|
30
|
+
}
|
|
31
|
+
export async function install() {
|
|
32
|
+
return mergeMcpServer(configPath(), "macula", {
|
|
33
|
+
command: "npx",
|
|
34
|
+
args: ["-y", "-p", "@macula-io/mcp", "macula-mcp"],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export async function uninstall() {
|
|
38
|
+
return removeMcpServer(configPath(), "macula");
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=claude_desktop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude_desktop.js","sourceRoot":"","sources":["../../../src/install/mcp_clients/claude_desktop.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,2BAA2B;AAC3B,6EAA6E;AAC7E,yDAAyD;AACzD,yDAAyD;AACzD,EAAE;AACF,gDAAgD;AAChD,oEAAoE;AAEpE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,cAAc,EACd,eAAe,GAEhB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE7C,MAAM,UAAU,UAAU;IACxB,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;IACrB,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnB,OAAO,IAAI,CACT,OAAO,EAAE,EACT,SAAS,EACT,qBAAqB,EACrB,QAAQ,EACR,4BAA4B,CAC7B,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;QAClB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;IAC/D,CAAC;IACD,0BAA0B;IAC1B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,OAAO,cAAc,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE;QAC5C,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,OAAO,eAAe,CAAC,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MergeResult } from "../config_merge.js";
|
|
2
|
+
export declare const CLIENT_ID = "cursor";
|
|
3
|
+
export declare const CLIENT_LABEL = "Cursor";
|
|
4
|
+
export declare function configPath(): string;
|
|
5
|
+
export declare function isInstalled(): boolean;
|
|
6
|
+
export declare function install(): Promise<MergeResult>;
|
|
7
|
+
export declare function uninstall(): Promise<MergeResult>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Cursor MCP client integration.
|
|
2
|
+
//
|
|
3
|
+
// Config path: ~/.cursor/mcp.json
|
|
4
|
+
// Schema: same { mcpServers: { ... } } as Claude Code.
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { mergeMcpServer, removeMcpServer, } from "../config_merge.js";
|
|
9
|
+
export const CLIENT_ID = "cursor";
|
|
10
|
+
export const CLIENT_LABEL = "Cursor";
|
|
11
|
+
export function configPath() {
|
|
12
|
+
return join(homedir(), ".cursor", "mcp.json");
|
|
13
|
+
}
|
|
14
|
+
export function isInstalled() {
|
|
15
|
+
return existsSync(join(homedir(), ".cursor"));
|
|
16
|
+
}
|
|
17
|
+
export async function install() {
|
|
18
|
+
// "-p <pkg> <bin>", not bare "npx -y @macula-io/mcp": this package
|
|
19
|
+
// has 4 bin entries and none is literally "mcp", so npx's default
|
|
20
|
+
// heuristic can't pick one -- see claude_code.ts's doc comment for
|
|
21
|
+
// the full story (found running a real packed tarball through npx).
|
|
22
|
+
return mergeMcpServer(configPath(), "macula", {
|
|
23
|
+
command: "npx",
|
|
24
|
+
args: ["-y", "-p", "@macula-io/mcp", "macula-mcp"],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function uninstall() {
|
|
28
|
+
return removeMcpServer(configPath(), "macula");
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../src/install/mcp_clients/cursor.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,kCAAkC;AAClC,uDAAuD;AAEvD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,cAAc,EACd,eAAe,GAEhB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAClC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,mEAAmE;IACnE,kEAAkE;IAClE,mEAAmE;IACnE,oEAAoE;IACpE,OAAO,cAAc,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE;QAC5C,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,OAAO,eAAe,CAAC,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MergeResult } from "../config_merge.js";
|
|
2
|
+
export interface ClientAdapter {
|
|
3
|
+
CLIENT_ID: string;
|
|
4
|
+
CLIENT_LABEL: string;
|
|
5
|
+
configPath: () => string;
|
|
6
|
+
isInstalled: () => boolean;
|
|
7
|
+
install: () => Promise<MergeResult>;
|
|
8
|
+
uninstall: () => Promise<MergeResult>;
|
|
9
|
+
}
|
|
10
|
+
export declare const ALL: ClientAdapter[];
|
|
11
|
+
export declare function detected(): ClientAdapter[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Registry of supported MCP clients. Used by bin/install,
|
|
2
|
+
// bin/uninstall, and bin/status to iterate over the install matrix
|
|
3
|
+
// without each command knowing about every client.
|
|
4
|
+
import * as claudeCode from "./claude_code.js";
|
|
5
|
+
import * as claudeDesktop from "./claude_desktop.js";
|
|
6
|
+
import * as cursor from "./cursor.js";
|
|
7
|
+
import * as windsurf from "./windsurf.js";
|
|
8
|
+
export const ALL = [claudeCode, claudeDesktop, cursor, windsurf];
|
|
9
|
+
export function detected() {
|
|
10
|
+
return ALL.filter((c) => c.isInstalled());
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/install/mcp_clients/index.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,mEAAmE;AACnE,mDAAmD;AAEnD,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,aAAa,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAY1C,MAAM,CAAC,MAAM,GAAG,GAAoB,CAAC,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAElF,MAAM,UAAU,QAAQ;IACtB,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type MergeResult } from "../config_merge.js";
|
|
2
|
+
export declare const CLIENT_ID = "windsurf";
|
|
3
|
+
export declare const CLIENT_LABEL = "Windsurf";
|
|
4
|
+
export declare function configPath(): string;
|
|
5
|
+
export declare function isInstalled(): boolean;
|
|
6
|
+
export declare function install(): Promise<MergeResult>;
|
|
7
|
+
export declare function uninstall(): Promise<MergeResult>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Windsurf MCP client integration.
|
|
2
|
+
//
|
|
3
|
+
// Config path: ~/.codeium/windsurf/mcp_config.json
|
|
4
|
+
// Schema: same { mcpServers: { ... } } as Claude Code.
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { mergeMcpServer, removeMcpServer, } from "../config_merge.js";
|
|
9
|
+
export const CLIENT_ID = "windsurf";
|
|
10
|
+
export const CLIENT_LABEL = "Windsurf";
|
|
11
|
+
export function configPath() {
|
|
12
|
+
return join(homedir(), ".codeium", "windsurf", "mcp_config.json");
|
|
13
|
+
}
|
|
14
|
+
export function isInstalled() {
|
|
15
|
+
return existsSync(join(homedir(), ".codeium", "windsurf"));
|
|
16
|
+
}
|
|
17
|
+
export async function install() {
|
|
18
|
+
// "-p <pkg> <bin>", not bare "npx -y @macula-io/mcp": this package
|
|
19
|
+
// has 4 bin entries and none is literally "mcp", so npx's default
|
|
20
|
+
// heuristic can't pick one -- see claude_code.ts's doc comment for
|
|
21
|
+
// the full story (found running a real packed tarball through npx).
|
|
22
|
+
return mergeMcpServer(configPath(), "macula", {
|
|
23
|
+
command: "npx",
|
|
24
|
+
args: ["-y", "-p", "@macula-io/mcp", "macula-mcp"],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function uninstall() {
|
|
28
|
+
return removeMcpServer(configPath(), "macula");
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=windsurf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windsurf.js","sourceRoot":"","sources":["../../../src/install/mcp_clients/windsurf.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,mDAAmD;AACnD,uDAAuD;AAEvD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EACL,cAAc,EACd,eAAe,GAEhB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEvC,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,mEAAmE;IACnE,kEAAkE;IAClE,mEAAmE;IACnE,oEAAoE;IACpE,OAAO,cAAc,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE;QAC5C,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,OAAO,eAAe,CAAC,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type OS = "linux" | "darwin" | "win32";
|
|
2
|
+
export type Arch = "x64" | "arm64";
|
|
3
|
+
export interface PlatformInfo {
|
|
4
|
+
os: OS;
|
|
5
|
+
arch: Arch;
|
|
6
|
+
/** Human-readable label used in install output. */
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function detect(): PlatformInfo;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Platform detection for the installer. Used to pick MCP-client
|
|
2
|
+
// config paths, which vary per OS. macula-cli's own install.sh/
|
|
3
|
+
// install.ps1 handle platform detection for fetching that binary --
|
|
4
|
+
// not this installer's job.
|
|
5
|
+
import { platform, arch } from "node:os";
|
|
6
|
+
export function detect() {
|
|
7
|
+
const rawOs = platform();
|
|
8
|
+
const rawArch = arch();
|
|
9
|
+
const os = mapOs(rawOs);
|
|
10
|
+
const cpu = mapArch(rawArch);
|
|
11
|
+
return {
|
|
12
|
+
os,
|
|
13
|
+
arch: cpu,
|
|
14
|
+
label: `${os}-${cpu}`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function mapOs(p) {
|
|
18
|
+
if (p === "linux")
|
|
19
|
+
return "linux";
|
|
20
|
+
if (p === "darwin")
|
|
21
|
+
return "darwin";
|
|
22
|
+
if (p === "win32")
|
|
23
|
+
return "win32";
|
|
24
|
+
throw new Error(`unsupported platform: ${p}. Macula supports linux, darwin, win32. ` +
|
|
25
|
+
`File an issue at codeberg.org/macula-io/macula-mcp if you need ${p}.`);
|
|
26
|
+
}
|
|
27
|
+
function mapArch(a) {
|
|
28
|
+
if (a === "x64")
|
|
29
|
+
return "x64";
|
|
30
|
+
if (a === "arm64")
|
|
31
|
+
return "arm64";
|
|
32
|
+
throw new Error(`unsupported arch: ${a}. Macula supports x64 and arm64.`);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=platform.js.map
|