@habitusnet/bc365 2.2.6 → 2.2.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.2.7] — 2026-02-20
6
+
7
+ ### Added
8
+ - `bc365 onboard --target claude-desktop` writes MCP servers directly into `claude_desktop_config.json`, merging with any existing config and preserving other keys
9
+ - `bc365 switch <profile> --target claude-desktop` does the same from a saved profile
10
+ - Both commands print "Restart Claude Desktop to apply changes." when using the claude-desktop target
11
+
5
12
  ## [2.2.6] — 2026-02-20
6
13
 
7
14
  ### Changed
package/lib/cli.js CHANGED
@@ -17,13 +17,14 @@ program
17
17
 
18
18
  program
19
19
  .command('onboard')
20
- .description('Auto-discover tenant/environment/company and register MCP servers with Claude Code')
20
+ .description('Auto-discover tenant/environment/company and register MCP servers')
21
21
  .option('-t, --tenant-id <id>', 'Azure AD tenant ID (skip Graph lookup)')
22
- .option('-s, --scope <scope>', 'Configuration scope: local, user, or project (default: local)', 'local')
22
+ .option('-s, --scope <scope>', 'Claude Code scope: local, user, or project (default: local)', 'local')
23
+ .option('--target <target>', 'Target: claude-code or claude-desktop (default: claude-code)', 'claude-code')
23
24
  .option('-p, --profile <name>', 'Profile name to save (default: tenantId/envName)')
24
25
  .action(async (opts) => {
25
26
  try {
26
- await onboard({ tenantId: opts.tenantId, scope: opts.scope, profileName: opts.profile });
27
+ await onboard({ tenantId: opts.tenantId, scope: opts.scope, target: opts.target, profileName: opts.profile });
27
28
  } catch (err) {
28
29
  console.error(chalk.red(`✗ ${err.message}`));
29
30
  process.exit(1);
@@ -33,7 +34,8 @@ program
33
34
  program
34
35
  .command('switch <profile>')
35
36
  .description('Switch active profile (re-registers MCP servers from saved profile)')
36
- .option('-s, --scope <scope>', 'Configuration scope: local, user, or project (default: local)', 'local')
37
+ .option('-s, --scope <scope>', 'Claude Code scope: local, user, or project (default: local)', 'local')
38
+ .option('--target <target>', 'Target: claude-code or claude-desktop (default: claude-code)', 'claude-code')
37
39
  .action(async (profileName, opts) => {
38
40
  try {
39
41
  const profile = await loadProfile(profileName);
@@ -41,15 +43,21 @@ program
41
43
  console.error(chalk.red(`✗ Profile '${profileName}' not found. Run 'bc365 onboard' first.`));
42
44
  process.exit(1);
43
45
  }
44
- const { execFile: _execFile } = await import('node:child_process');
45
- const { promisify } = await import('node:util');
46
- const execFile = promisify(_execFile);
47
- const { buildMcpConfig } = await import('./onboard.js');
46
+ const { buildMcpConfig, registerWithClaudeDesktop } = await import('./onboard.js');
48
47
  const config = buildMcpConfig(profile);
49
- for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
50
- await execFile('claude', ['mcp', 'add-json', '-s', opts.scope, name, JSON.stringify(serverConfig)]);
48
+ if (opts.target === 'claude-desktop') {
49
+ const configPath = await registerWithClaudeDesktop(config);
50
+ console.log(chalk.green(`✓ Switched to profile '${profileName}' → ${configPath}`));
51
+ console.log('Restart Claude Desktop to apply changes.');
52
+ } else {
53
+ const { execFile: _execFile } = await import('node:child_process');
54
+ const { promisify } = await import('node:util');
55
+ const execFile = promisify(_execFile);
56
+ for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
57
+ await execFile('claude', ['mcp', 'add-json', '-s', opts.scope, name, JSON.stringify(serverConfig)]);
58
+ }
59
+ console.log(chalk.green(`✓ Switched to profile '${profileName}' (scope: ${opts.scope})`));
51
60
  }
52
- console.log(chalk.green(`✓ Switched to profile '${profileName}' (scope: ${opts.scope})`));
53
61
  } catch (err) {
54
62
  console.error(chalk.red(`✗ ${err.message}`));
55
63
  process.exit(1);
package/lib/onboard.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { execFile as _execFile } from 'node:child_process';
2
+ import { readFile, writeFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
2
5
  import { promisify } from 'node:util';
3
6
  import inquirer from 'inquirer';
4
7
  import { getToken } from './auth.js';
@@ -8,6 +11,13 @@ import { saveProfile } from './profiles.js';
8
11
  const execFile = promisify(_execFile);
9
12
  const BC_API_BASE = 'https://api.businesscentral.dynamics.com/v2.0';
10
13
 
14
+ export function claudeDesktopConfigPath() {
15
+ if (process.platform === 'win32') {
16
+ return join(process.env.APPDATA, 'Claude', 'claude_desktop_config.json');
17
+ }
18
+ return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
19
+ }
20
+
11
21
  export function buildMcpConfig(ctx) {
12
22
  const { tenantId, envName, companyId } = ctx;
13
23
  return {
@@ -31,8 +41,24 @@ export function buildMcpConfig(ctx) {
31
41
  };
32
42
  }
33
43
 
44
+ export async function registerWithClaudeDesktop(config) {
45
+ const configPath = claudeDesktopConfigPath();
46
+ let existing = {};
47
+ try {
48
+ existing = JSON.parse(await readFile(configPath, 'utf8'));
49
+ } catch {
50
+ // file doesn't exist yet — start fresh
51
+ }
52
+ const merged = {
53
+ ...existing,
54
+ mcpServers: { ...(existing.mcpServers ?? {}), ...config.mcpServers },
55
+ };
56
+ await writeFile(configPath, JSON.stringify(merged, null, 2), 'utf8');
57
+ return configPath;
58
+ }
59
+
34
60
  export async function onboard(options = {}) {
35
- const { tenantId, scope = 'local', profileName } = options;
61
+ const { tenantId, scope = 'local', target = 'claude-code', profileName } = options;
36
62
  const token = await getToken();
37
63
 
38
64
  const environments = await getEnvironments(token, { tenantId, type: 'Production' });
@@ -68,10 +94,16 @@ export async function onboard(options = {}) {
68
94
  const ctx = { tenantId: selectedEnv.aadTenantId, envName, companyId };
69
95
  const config = buildMcpConfig(ctx);
70
96
 
71
- for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
72
- await execFile('claude', ['mcp', 'add-json', '-s', scope, name, JSON.stringify(serverConfig)]);
97
+ if (target === 'claude-desktop') {
98
+ const configPath = await registerWithClaudeDesktop(config);
99
+ console.log(`✓ Wrote MCP servers to ${configPath}`);
100
+ console.log('Restart Claude Desktop to apply changes.');
101
+ } else {
102
+ for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
103
+ await execFile('claude', ['mcp', 'add-json', '-s', scope, name, JSON.stringify(serverConfig)]);
104
+ }
105
+ console.log(`✓ Registered MCP servers (scope: ${scope})`);
73
106
  }
74
- console.log(`✓ Registered MCP servers (scope: ${scope})`);
75
107
 
76
108
  const name = profileName ?? `${selectedEnv.aadTenantId}/${envName}`;
77
109
  await saveProfile(name, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@habitusnet/bc365",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "description": "Smart onboarding CLI and MCP config manager for Business Central",
5
5
  "type": "module",
6
6
  "bin": {