@anaclumos/taal 1.1.2

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.
Files changed (42) hide show
  1. package/.claude/settings.json +15 -0
  2. package/.github/workflows/ci.yml +28 -0
  3. package/.github/workflows/publish.yml +42 -0
  4. package/AGENTS.md +123 -0
  5. package/README.md +568 -0
  6. package/biome.jsonc +24 -0
  7. package/lefthook.yml +12 -0
  8. package/package.json +52 -0
  9. package/src/commands/collect.ts +172 -0
  10. package/src/commands/diff.ts +127 -0
  11. package/src/commands/init.ts +66 -0
  12. package/src/commands/list.ts +80 -0
  13. package/src/commands/providers.ts +46 -0
  14. package/src/commands/sync.ts +111 -0
  15. package/src/commands/validate.ts +17 -0
  16. package/src/config/env.ts +49 -0
  17. package/src/config/loader.ts +88 -0
  18. package/src/config/parser.ts +44 -0
  19. package/src/config/schema.ts +67 -0
  20. package/src/errors/index.ts +43 -0
  21. package/src/index.ts +301 -0
  22. package/src/providers/antigravity.ts +24 -0
  23. package/src/providers/base.ts +70 -0
  24. package/src/providers/claude-code.ts +12 -0
  25. package/src/providers/claude-desktop.ts +19 -0
  26. package/src/providers/codex.ts +61 -0
  27. package/src/providers/continue.ts +62 -0
  28. package/src/providers/cursor.ts +25 -0
  29. package/src/providers/index.ts +34 -0
  30. package/src/providers/opencode.ts +42 -0
  31. package/src/providers/registry.ts +74 -0
  32. package/src/providers/types.ts +99 -0
  33. package/src/providers/utils.ts +106 -0
  34. package/src/providers/windsurf.ts +50 -0
  35. package/src/providers/zed.ts +35 -0
  36. package/src/scripts/generate-schema.ts +17 -0
  37. package/src/skills/copy.ts +58 -0
  38. package/src/skills/discovery.ts +87 -0
  39. package/src/skills/validator.ts +95 -0
  40. package/src/utils/atomic-write.ts +35 -0
  41. package/src/utils/backup.ts +27 -0
  42. package/taal.schema.json +91 -0
@@ -0,0 +1,88 @@
1
+ import { exists, readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { parse } from "yaml";
5
+ import { findEnvVarReferences, substituteEnvVars } from "./env.js";
6
+ import { type TaalConfig, TaalConfigSchema } from "./schema.js";
7
+
8
+ export interface LoadConfigResult {
9
+ config: TaalConfig | null;
10
+ errors: string[];
11
+ warnings: string[];
12
+ }
13
+
14
+ export async function loadTaalConfig(
15
+ baseDir?: string
16
+ ): Promise<LoadConfigResult> {
17
+ const home = baseDir || homedir();
18
+ const configPath = join(home, ".taal", "config.yaml");
19
+
20
+ const errors: string[] = [];
21
+ const warnings: string[] = [];
22
+
23
+ if (!(await exists(configPath))) {
24
+ return {
25
+ config: null,
26
+ errors: ["Config file not found"],
27
+ warnings: [],
28
+ };
29
+ }
30
+
31
+ let content: string;
32
+ try {
33
+ content = await readFile(configPath, "utf-8");
34
+ } catch (error) {
35
+ return {
36
+ config: null,
37
+ errors: [
38
+ `Failed to read config: ${error instanceof Error ? error.message : error}`,
39
+ ],
40
+ warnings: [],
41
+ };
42
+ }
43
+
44
+ let rawConfig: unknown;
45
+ try {
46
+ rawConfig = parse(content);
47
+ } catch (error) {
48
+ return {
49
+ config: null,
50
+ errors: [
51
+ `Failed to parse YAML: ${error instanceof Error ? error.message : error}`,
52
+ ],
53
+ warnings,
54
+ };
55
+ }
56
+
57
+ const envVarRefs = findEnvVarReferences(rawConfig);
58
+ for (const varName of envVarRefs) {
59
+ if (!process.env[varName]) {
60
+ warnings.push(`Environment variable ${varName} is not set`);
61
+ }
62
+ }
63
+
64
+ const configWithEnv = substituteEnvVars(rawConfig);
65
+ const result = TaalConfigSchema.safeParse(configWithEnv);
66
+
67
+ if (!result.success) {
68
+ for (const issue of result.error.issues) {
69
+ errors.push(`${issue.path.join(".")}: ${issue.message}`);
70
+ }
71
+ return {
72
+ config: null,
73
+ errors,
74
+ warnings,
75
+ };
76
+ }
77
+
78
+ return {
79
+ config: result.data,
80
+ errors: [],
81
+ warnings,
82
+ };
83
+ }
84
+
85
+ export function getConfigPath(baseDir?: string): string {
86
+ const home = baseDir || homedir();
87
+ return join(home, ".taal", "config.yaml");
88
+ }
@@ -0,0 +1,44 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { parse } from "yaml";
3
+ import { substituteEnvVars } from "./env.js";
4
+ import { type TaalConfig, TaalConfigSchema } from "./schema.js";
5
+
6
+ /**
7
+ * Loads and validates TAAL configuration from a YAML file
8
+ *
9
+ * @param path - Path to the YAML configuration file
10
+ * @returns Validated TaalConfig object
11
+ * @throws Error if file cannot be read, YAML is invalid, or validation fails
12
+ */
13
+ export function loadConfig(path: string): TaalConfig {
14
+ // 1. Read file
15
+ let fileContent: string;
16
+ try {
17
+ fileContent = readFileSync(path, "utf-8");
18
+ } catch (error) {
19
+ throw new Error(`Failed to read config file at ${path}: ${error}`);
20
+ }
21
+
22
+ // 2. Parse YAML
23
+ let rawConfig: unknown;
24
+ try {
25
+ rawConfig = parse(fileContent);
26
+ } catch (error) {
27
+ throw new Error(`Failed to parse YAML in ${path}: ${error}`);
28
+ }
29
+
30
+ // 3. Substitute environment variables
31
+ const configWithEnv = substituteEnvVars(rawConfig);
32
+
33
+ // 4. Validate with Zod
34
+ const result = TaalConfigSchema.safeParse(configWithEnv);
35
+
36
+ if (!result.success) {
37
+ const errors = result.error.issues
38
+ .map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`)
39
+ .join("\n");
40
+ throw new Error(`Config validation failed:\n${errors}`);
41
+ }
42
+
43
+ return result.data;
44
+ }
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * MCP Server Schema
5
+ * Supports either stdio (command-based) OR HTTP (url-based) servers, not both
6
+ */
7
+ export const McpServerSchema = z
8
+ .object({
9
+ // Stdio server fields
10
+ command: z.string().optional(),
11
+ args: z.array(z.string()).optional(),
12
+ env: z.record(z.string()).optional(),
13
+
14
+ // HTTP server fields
15
+ url: z.string().optional(),
16
+ headers: z.record(z.string()).optional(),
17
+
18
+ // Provider-specific overrides
19
+ overrides: z
20
+ .record(
21
+ z.object({
22
+ enabled_tools: z.array(z.string()).optional(),
23
+ })
24
+ )
25
+ .optional(),
26
+ })
27
+ .refine(
28
+ (data) => {
29
+ const hasStdio = !!data.command;
30
+ const hasHttp = !!data.url;
31
+ // Must be either stdio OR http, not both, not neither
32
+ return (hasStdio && !hasHttp) || (hasHttp && !hasStdio);
33
+ },
34
+ {
35
+ message: "Server must be either stdio (command) or http (url), not both",
36
+ }
37
+ );
38
+
39
+ /**
40
+ * Skills Configuration Schema
41
+ */
42
+ export const SkillsConfigSchema = z.object({
43
+ paths: z.array(z.string()),
44
+ });
45
+
46
+ /**
47
+ * Providers Configuration Schema
48
+ */
49
+ export const ProvidersConfigSchema = z.object({
50
+ enabled: z.array(z.string()),
51
+ });
52
+
53
+ /**
54
+ * TAAL Configuration Schema
55
+ */
56
+ export const TaalConfigSchema = z.object({
57
+ version: z.literal("1"),
58
+ mcp: z.record(McpServerSchema).optional(),
59
+ skills: SkillsConfigSchema.optional(),
60
+ providers: ProvidersConfigSchema.optional(),
61
+ });
62
+
63
+ // Export inferred types
64
+ export type McpServer = z.infer<typeof McpServerSchema>;
65
+ export type SkillsConfig = z.infer<typeof SkillsConfigSchema>;
66
+ export type ProvidersConfig = z.infer<typeof ProvidersConfigSchema>;
67
+ export type TaalConfig = z.infer<typeof TaalConfigSchema>;
@@ -0,0 +1,43 @@
1
+ export class TaalError extends Error {
2
+ readonly code: string;
3
+
4
+ constructor(message: string, code: string) {
5
+ super(message);
6
+ this.name = "TaalError";
7
+ this.code = code;
8
+ }
9
+ }
10
+
11
+ export class ConfigError extends TaalError {
12
+ constructor(message: string) {
13
+ super(message, "CONFIG_ERROR");
14
+ this.name = "ConfigError";
15
+ }
16
+ }
17
+
18
+ export class ProviderError extends TaalError {
19
+ readonly providerName: string;
20
+
21
+ constructor(message: string, providerName: string) {
22
+ super(message, "PROVIDER_ERROR");
23
+ this.name = "ProviderError";
24
+ this.providerName = providerName;
25
+ }
26
+ }
27
+
28
+ export class ValidationError extends TaalError {
29
+ readonly errors: string[];
30
+
31
+ constructor(message: string, errors: string[]) {
32
+ super(message, "VALIDATION_ERROR");
33
+ this.name = "ValidationError";
34
+ this.errors = errors;
35
+ }
36
+ }
37
+
38
+ export function formatError(error: unknown): string {
39
+ if (error instanceof Error) {
40
+ return error.message;
41
+ }
42
+ return String(error);
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env bun
2
+ import { exists, readFile, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import chalk from "chalk";
6
+ import { Command } from "commander";
7
+ import YAML from "yaml";
8
+ import { collect } from "./commands/collect";
9
+ import { diff } from "./commands/diff";
10
+ import { init } from "./commands/init";
11
+ import { list } from "./commands/list";
12
+ import { providers } from "./commands/providers";
13
+ import { sync } from "./commands/sync";
14
+ import { validate } from "./commands/validate";
15
+
16
+ const program = new Command();
17
+
18
+ program
19
+ .name("taal")
20
+ .description(
21
+ "CLI to sync MCP server configs and Agent Skills across AI providers"
22
+ )
23
+ .version("1.0.0");
24
+
25
+ program
26
+ .command("init")
27
+ .description("Initialize TAAL configuration")
28
+ .option("-f, --force", "Overwrite existing configuration")
29
+ .action(async (options) => {
30
+ try {
31
+ await init(homedir(), options);
32
+ console.log("✓ TAAL initialized successfully");
33
+ console.log(` Config: ${homedir()}/.taal/config.yaml`);
34
+ console.log(` Skills: ${homedir()}/.taal/skills/`);
35
+ } catch (error) {
36
+ console.error("Error:", error instanceof Error ? error.message : error);
37
+ process.exit(1);
38
+ }
39
+ });
40
+
41
+ program
42
+ .command("collect")
43
+ .description("Import existing MCP configs from installed providers")
44
+ .action(async () => {
45
+ try {
46
+ console.log("Scanning installed providers...");
47
+ const result = await collect();
48
+
49
+ console.log(
50
+ `\n✓ Found ${result.summary.totalServers} servers from ${result.summary.providersWithConfigs} providers`
51
+ );
52
+
53
+ if (result.conflicts.length > 0) {
54
+ console.log("\n⚠ Conflicts detected:");
55
+ for (const conflict of result.conflicts) {
56
+ console.log(
57
+ ` - "${conflict.serverName}" found in: ${conflict.providers.join(", ")}`
58
+ );
59
+ }
60
+ }
61
+
62
+ const configPath = join(homedir(), ".taal", "config.yaml");
63
+ interface TaalConfigFile {
64
+ version: string;
65
+ mcp: Record<string, unknown>;
66
+ skills?: { paths: string[] };
67
+ providers?: { enabled: string[] };
68
+ }
69
+
70
+ let existingConfig: TaalConfigFile = {
71
+ version: "1",
72
+ mcp: {},
73
+ skills: { paths: ["~/.taal/skills"] },
74
+ providers: { enabled: [] },
75
+ };
76
+
77
+ if (await exists(configPath)) {
78
+ const content = await readFile(configPath, "utf-8");
79
+ existingConfig = YAML.parse(content) as TaalConfigFile;
80
+ }
81
+
82
+ existingConfig.mcp = { ...existingConfig.mcp, ...result.servers };
83
+
84
+ await writeFile(configPath, YAML.stringify(existingConfig), "utf-8");
85
+ console.log(`\n✓ Updated config: ${configPath}`);
86
+ } catch (error) {
87
+ console.error("Error:", error instanceof Error ? error.message : error);
88
+ process.exit(1);
89
+ }
90
+ });
91
+
92
+ program
93
+ .command("validate")
94
+ .description("Validate TAAL configuration")
95
+ .action(async () => {
96
+ try {
97
+ const result = await validate();
98
+
99
+ if (result.warnings.length > 0) {
100
+ console.log(chalk.yellow("\nWarnings:"));
101
+ for (const warning of result.warnings) {
102
+ console.log(chalk.yellow(` ⚠ ${warning}`));
103
+ }
104
+ }
105
+
106
+ if (result.valid) {
107
+ console.log(chalk.green("\n✓ Configuration is valid"));
108
+ process.exit(0);
109
+ } else {
110
+ console.log(chalk.red("\n✗ Configuration is invalid\n"));
111
+ console.log(chalk.red("Errors:"));
112
+ for (const error of result.errors) {
113
+ console.log(chalk.red(` • ${error}`));
114
+ }
115
+ process.exit(1);
116
+ }
117
+ } catch (error) {
118
+ console.error("Error:", error instanceof Error ? error.message : error);
119
+ process.exit(1);
120
+ }
121
+ });
122
+
123
+ program
124
+ .command("diff [provider]")
125
+ .description("Show what would change without writing")
126
+ .action(async (provider?: string) => {
127
+ try {
128
+ const result = await diff(undefined, provider);
129
+
130
+ if (result.error) {
131
+ console.error(chalk.red(`Error: ${result.error}`));
132
+ process.exit(1);
133
+ }
134
+
135
+ if (!result.hasChanges) {
136
+ console.log(chalk.green("\n✓ No changes detected"));
137
+ process.exit(0);
138
+ }
139
+
140
+ console.log(chalk.bold("\nChanges:\n"));
141
+
142
+ const byProvider = new Map<string, typeof result.changes>();
143
+ for (const change of result.changes) {
144
+ if (!byProvider.has(change.provider)) {
145
+ byProvider.set(change.provider, []);
146
+ }
147
+ byProvider.get(change.provider)?.push(change);
148
+ }
149
+
150
+ for (const [providerName, changes] of byProvider) {
151
+ console.log(chalk.bold(`${providerName}:`));
152
+ for (const change of changes) {
153
+ if (change.type === "add") {
154
+ console.log(chalk.green(` + ${change.serverName}`));
155
+ } else if (change.type === "remove") {
156
+ console.log(chalk.red(` - ${change.serverName}`));
157
+ } else {
158
+ console.log(chalk.yellow(` ~ ${change.serverName}`));
159
+ }
160
+ }
161
+ console.log();
162
+ }
163
+
164
+ process.exit(1);
165
+ } catch (error) {
166
+ console.error("Error:", error instanceof Error ? error.message : error);
167
+ process.exit(1);
168
+ }
169
+ });
170
+
171
+ program
172
+ .command("sync [provider]")
173
+ .description("Sync MCP configs and skills to providers")
174
+ .action(async (provider?: string) => {
175
+ try {
176
+ console.log(chalk.bold("Syncing..."));
177
+ const result = await sync(undefined, provider);
178
+
179
+ if (result.error) {
180
+ console.error(chalk.red(`\nError: ${result.error}`));
181
+ process.exit(1);
182
+ }
183
+
184
+ if (result.synced.length > 0) {
185
+ console.log(
186
+ chalk.green(`\n✓ Synced to ${result.synced.length} provider(s):`)
187
+ );
188
+ for (const p of result.synced) {
189
+ console.log(chalk.green(` • ${p}`));
190
+ }
191
+ }
192
+
193
+ if (result.failed.length > 0) {
194
+ console.log(
195
+ chalk.red(`\n✗ Failed to sync ${result.failed.length} provider(s):`)
196
+ );
197
+ for (const f of result.failed) {
198
+ console.log(chalk.red(` • ${f.provider}: ${f.error}`));
199
+ }
200
+ }
201
+
202
+ process.exit(result.success ? 0 : 1);
203
+ } catch (error) {
204
+ console.error("Error:", error instanceof Error ? error.message : error);
205
+ process.exit(1);
206
+ }
207
+ });
208
+
209
+ program
210
+ .command("list")
211
+ .description("List configured MCP servers and skills")
212
+ .action(async () => {
213
+ try {
214
+ const result = await list();
215
+
216
+ if (result.error) {
217
+ console.error(chalk.red(`Error: ${result.error}`));
218
+ process.exit(1);
219
+ }
220
+
221
+ console.log(chalk.bold("\nMCP Servers:\n"));
222
+ if (result.servers.length === 0) {
223
+ console.log(chalk.dim(" No servers configured"));
224
+ } else {
225
+ for (const server of result.servers) {
226
+ const type =
227
+ server.type === "stdio"
228
+ ? chalk.blue("[stdio]")
229
+ : chalk.green("[http]");
230
+ const detail = server.command || server.url || "";
231
+ console.log(
232
+ ` ${type} ${chalk.bold(server.name)} ${chalk.dim(detail)}`
233
+ );
234
+ }
235
+ }
236
+
237
+ console.log(chalk.bold("\nSkills:\n"));
238
+ if (result.skills.length === 0) {
239
+ console.log(chalk.dim(" No skills found"));
240
+ } else {
241
+ for (const skill of result.skills) {
242
+ console.log(` • ${chalk.bold(skill.name)} ${chalk.dim(skill.path)}`);
243
+ }
244
+ }
245
+
246
+ console.log(chalk.bold("\nEnabled Providers:\n"));
247
+ if (result.enabledProviders.length === 0) {
248
+ console.log(chalk.dim(" No providers enabled"));
249
+ } else {
250
+ for (const provider of result.enabledProviders) {
251
+ console.log(` • ${provider}`);
252
+ }
253
+ }
254
+
255
+ console.log();
256
+ } catch (error) {
257
+ console.error("Error:", error instanceof Error ? error.message : error);
258
+ process.exit(1);
259
+ }
260
+ });
261
+
262
+ program
263
+ .command("providers")
264
+ .description("List all supported providers")
265
+ .action(async () => {
266
+ try {
267
+ const result = await providers();
268
+
269
+ console.log(chalk.bold("\nSupported Providers:\n"));
270
+
271
+ for (const provider of result.providers) {
272
+ const status: string[] = [];
273
+
274
+ if (provider.installed) {
275
+ status.push(chalk.green("installed"));
276
+ } else {
277
+ status.push(chalk.dim("not installed"));
278
+ }
279
+
280
+ if (provider.enabled) {
281
+ status.push(chalk.blue("enabled"));
282
+ } else {
283
+ status.push(chalk.dim("disabled"));
284
+ }
285
+
286
+ const format = chalk.yellow(`[${provider.format}]`);
287
+
288
+ console.log(
289
+ ` ${chalk.bold(provider.name)} ${format} ${status.join(", ")}`
290
+ );
291
+ console.log(chalk.dim(` ${provider.configPath}`));
292
+ }
293
+
294
+ console.log();
295
+ } catch (error) {
296
+ console.error("Error:", error instanceof Error ? error.message : error);
297
+ process.exit(1);
298
+ }
299
+ });
300
+
301
+ program.parse();
@@ -0,0 +1,24 @@
1
+ import { platform } from "node:os";
2
+ import { join } from "node:path";
3
+ import { BaseProvider, createStdioTransformer } from "./base.js";
4
+
5
+ export class AntigravityProvider extends BaseProvider {
6
+ name = "antigravity";
7
+ configPath = (home: string) => {
8
+ const os = platform();
9
+ if (os === "darwin") {
10
+ return join(
11
+ home,
12
+ "Library",
13
+ "Application Support",
14
+ "Antigravity",
15
+ "settings.json"
16
+ );
17
+ }
18
+ return join(home, ".config", "antigravity", "settings.json");
19
+ };
20
+ format = "json" as const;
21
+ mcpKey = "mcpServers";
22
+
23
+ transformMcpServers = createStdioTransformer("Antigravity");
24
+ }
@@ -0,0 +1,70 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname } from "node:path";
4
+ import type { McpServer } from "../config/schema.js";
5
+ import type { ConfigFormat, Provider } from "./types.js";
6
+ import {
7
+ readConfig,
8
+ resolveConfigPath,
9
+ writeConfig as writeConfigUtil,
10
+ } from "./utils.js";
11
+
12
+ export abstract class BaseProvider implements Provider {
13
+ abstract name: string;
14
+ abstract configPath: string | ((home: string) => string);
15
+ abstract format: ConfigFormat;
16
+ abstract mcpKey: string;
17
+ skillsPath?: string | ((home: string) => string);
18
+
19
+ async isInstalled(home?: string): Promise<boolean> {
20
+ const homeDir = home || homedir();
21
+ const configDir = dirname(resolveConfigPath(this.configPath, homeDir));
22
+ return existsSync(configDir);
23
+ }
24
+
25
+ async readConfig(home?: string): Promise<unknown> {
26
+ const homeDir = home || homedir();
27
+ const path = resolveConfigPath(this.configPath, homeDir);
28
+ return readConfig(path, this.format);
29
+ }
30
+
31
+ async writeConfig(config: unknown, home?: string): Promise<void> {
32
+ const homeDir = home || homedir();
33
+ const path = resolveConfigPath(this.configPath, homeDir);
34
+ writeConfigUtil(path, this.format, config);
35
+ }
36
+
37
+ abstract transformMcpServers(
38
+ servers: Record<string, McpServer>
39
+ ): Record<string, unknown> | unknown[];
40
+ }
41
+
42
+ export function createStdioTransformer(providerName: string) {
43
+ return function transformMcpServers(
44
+ servers: Record<string, McpServer>
45
+ ): Record<string, unknown> {
46
+ const transformed: Record<string, unknown> = {};
47
+
48
+ for (const [name, server] of Object.entries(servers)) {
49
+ if (server.url) {
50
+ console.warn(
51
+ `Skipping HTTP server "${name}" - ${providerName} only supports stdio servers`
52
+ );
53
+ continue;
54
+ }
55
+
56
+ if (!server.command) {
57
+ console.warn(`Skipping server "${name}" - missing command`);
58
+ continue;
59
+ }
60
+
61
+ transformed[name] = {
62
+ command: server.command,
63
+ ...(server.args && { args: server.args }),
64
+ ...(server.env && { env: server.env }),
65
+ };
66
+ }
67
+
68
+ return transformed;
69
+ };
70
+ }
@@ -0,0 +1,12 @@
1
+ import { join } from "node:path";
2
+ import { BaseProvider, createStdioTransformer } from "./base.js";
3
+
4
+ export class ClaudeCodeProvider extends BaseProvider {
5
+ name = "claude-code";
6
+ configPath = (home: string) => join(home, ".claude", "settings.json");
7
+ format = "json" as const;
8
+ mcpKey = "mcpServers";
9
+ skillsPath = (home: string) => join(home, ".claude", "skills");
10
+
11
+ transformMcpServers = createStdioTransformer("Claude Code");
12
+ }
@@ -0,0 +1,19 @@
1
+ import { join } from "node:path";
2
+ import { BaseProvider, createStdioTransformer } from "./base.js";
3
+
4
+ export class ClaudeDesktopProvider extends BaseProvider {
5
+ name = "claude-desktop";
6
+ configPath = (home: string) =>
7
+ join(
8
+ home,
9
+ "Library",
10
+ "Application Support",
11
+ "Claude",
12
+ "claude_desktop_config.json"
13
+ );
14
+ format = "json" as const;
15
+ mcpKey = "mcpServers";
16
+ skillsPath = (home: string) => join(home, ".claude", "skills");
17
+
18
+ transformMcpServers = createStdioTransformer("Claude Desktop");
19
+ }