bcdocker 1.0.0 → 1.0.1

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.
@@ -0,0 +1,147 @@
1
+ import { runPowerShell, runRawPowerShell, psEscape } from "./executor.js";
2
+ function mcpText(text) {
3
+ return { content: [{ type: "text", text }] };
4
+ }
5
+ export async function handleListContainers() {
6
+ const result = await runRawPowerShell(`
7
+ Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
8
+ $containers = Get-BcContainers
9
+ if ($containers.Count -eq 0) {
10
+ Write-Output "No BC containers found."
11
+ } else {
12
+ foreach ($name in $containers) {
13
+ $status = docker inspect $name --format '{{.State.Status}}' 2>$null
14
+ Write-Output "$name [$status]"
15
+ }
16
+ }
17
+ `);
18
+ return mcpText(result.stdout || "No containers found.");
19
+ }
20
+ export async function handleContainerInfo({ containerName }) {
21
+ const cn = psEscape(containerName);
22
+ const result = await runPowerShell(`
23
+ Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
24
+ $bcVersion = Get-BcContainerNavVersion -containerOrImageName '${cn}'
25
+ $webUrl = Get-BCDockerWebClientUrl -ContainerName '${cn}' -UseHttps:$false
26
+ $status = docker inspect '${cn}' --format '{{.State.Status}}' 2>$null
27
+ Write-Output "Container : ${cn}"
28
+ Write-Output "Status : $status"
29
+ Write-Output "BC Version : $bcVersion"
30
+ Write-Output "Web Client : $webUrl"
31
+ Write-Output "OData/API : http://${cn}:7048/BC/api"
32
+ Write-Output "Dev Service: http://${cn}:7049/BC"
33
+ `);
34
+ return mcpText(result.stdout);
35
+ }
36
+ export async function handleCreateContainer({ containerName, version, country, userName, password, memoryLimit, isolation, testToolkit, bypassCDN, licenseFile, }) {
37
+ const includeToolkit = testToolkit !== "none" ? "$true" : "$false";
38
+ const libOnly = testToolkit === "libraries" ? "$true" : "$false";
39
+ const cdn = bypassCDN ? "-BypassCDN" : "";
40
+ const lic = licenseFile ? `-LicenseFile '${psEscape(licenseFile)}'` : "";
41
+ const result = await runPowerShell(`
42
+ New-BCDContainer \`
43
+ -ContainerName '${psEscape(containerName)}' \`
44
+ -Version '${psEscape(version)}' \`
45
+ -Country '${psEscape(country)}' \`
46
+ -UserName '${psEscape(userName)}' \`
47
+ -Password '${psEscape(password)}' \`
48
+ -MemoryLimit '${psEscape(memoryLimit)}' \`
49
+ -Isolation '${psEscape(isolation)}' \`
50
+ -IncludeTestToolkit ${includeToolkit} \`
51
+ -TestLibrariesOnly ${libOnly} \`
52
+ ${cdn} ${lic}
53
+ `, 1_800_000);
54
+ return mcpText(result.stdout || "Container creation complete.");
55
+ }
56
+ export async function handleRemoveContainer({ containerName }) {
57
+ const result = await runPowerShell(`Remove-BCDContainer -ContainerName '${psEscape(containerName)}'`);
58
+ return mcpText(result.stdout || `Container '${psEscape(containerName)}' removed.`);
59
+ }
60
+ export async function handleStartContainer({ containerName }) {
61
+ const result = await runPowerShell(`Start-BCDContainer -ContainerName '${psEscape(containerName)}'`);
62
+ return mcpText(result.stdout || `Container '${psEscape(containerName)}' started.`);
63
+ }
64
+ export async function handleStopContainer({ containerName }) {
65
+ const result = await runPowerShell(`Stop-BCDContainer -ContainerName '${psEscape(containerName)}'`);
66
+ return mcpText(result.stdout || `Container '${psEscape(containerName)}' stopped.`);
67
+ }
68
+ export async function handleRestartContainer({ containerName }) {
69
+ const result = await runPowerShell(`Restart-BCDContainer -ContainerName '${psEscape(containerName)}'`);
70
+ return mcpText(result.stdout || `Container '${psEscape(containerName)}' restarted.`);
71
+ }
72
+ export async function handleOpenWebclient({ containerName }) {
73
+ const result = await runPowerShell(`
74
+ $url = Get-BCDockerWebClientUrl -ContainerName '${psEscape(containerName)}' -UseHttps:$false
75
+ Write-Output $url
76
+ `);
77
+ return mcpText(result.stdout || "Could not resolve URL.");
78
+ }
79
+ export async function handleListApps({ containerName, publisher, }) {
80
+ const cn = psEscape(containerName);
81
+ const filter = publisher ? `| Where-Object { $_.Publisher -eq '${psEscape(publisher)}' }` : "";
82
+ const result = await runRawPowerShell(`
83
+ Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
84
+ $apps = Get-BcContainerAppInfo -containerName '${cn}' -tenant default -tenantSpecificProperties ${filter}
85
+ $apps | Select-Object Name, Publisher, Version, IsInstalled, Scope |
86
+ Sort-Object Publisher, Name |
87
+ Format-Table -AutoSize | Out-String -Width 200
88
+ `);
89
+ return mcpText(result.stdout || "No apps found.");
90
+ }
91
+ export async function handleInstallApp({ containerName, appFile, userName, password, }) {
92
+ const result = await runPowerShell(`
93
+ $cred = Get-BCCredential -UserName '${psEscape(userName)}' -Password '${psEscape(password)}'
94
+ Install-BCDApp -ContainerName '${psEscape(containerName)}' -AppFile '${psEscape(appFile)}' -Credential $cred
95
+ `);
96
+ return mcpText(result.stdout || "App installed.");
97
+ }
98
+ export async function handleUninstallApp({ containerName, appName, appPublisher, }) {
99
+ const cn = psEscape(containerName);
100
+ const an = psEscape(appName);
101
+ const ap = psEscape(appPublisher);
102
+ const result = await runRawPowerShell(`
103
+ Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
104
+ $allApps = Get-BcContainerAppInfo -containerName '${cn}' -tenant default -tenantSpecificProperties
105
+ $sorted = Get-BcContainerAppInfo -containerName '${cn}' -tenant default -tenantSpecificProperties -sort DependenciesLast
106
+ $target = $allApps | Where-Object { $_.Name -eq '${an}' -and $_.Publisher -eq '${ap}' }
107
+ if (-not $target) { Write-Output "App '${an}' by '${ap}' not found."; exit }
108
+ foreach ($app in $sorted) {
109
+ if ($app.Name -eq '${an}' -and $app.Publisher -eq '${ap}' -and $app.IsInstalled) {
110
+ UnInstall-BcContainerApp -name $app.Name -containerName '${cn}' -publisher $app.Publisher -version $app.Version -force
111
+ Write-Output "Uninstalled: $($app.Name) v$($app.Version)"
112
+ }
113
+ }
114
+ `);
115
+ return mcpText(result.stdout || "Uninstall complete.");
116
+ }
117
+ export async function handlePublishProject({ containerName, projectFolder, userName, password, }) {
118
+ const result = await runPowerShell(`
119
+ $cred = Get-BCCredential -UserName '${psEscape(userName)}' -Password '${psEscape(password)}'
120
+ Publish-BCDProject -ContainerName '${psEscape(containerName)}' -ProjectFolder '${psEscape(projectFolder)}' -Credential $cred
121
+ `, 300_000);
122
+ return mcpText(result.stdout || "Project compiled and published.");
123
+ }
124
+ export async function handleImportTestToolkit({ containerName, librariesOnly, userName, password, }) {
125
+ const libPs = librariesOnly ? "$true" : "$false";
126
+ const result = await runPowerShell(`
127
+ $cred = Get-BCCredential -UserName '${psEscape(userName)}' -Password '${psEscape(password)}'
128
+ Import-BCDTestToolkit -ContainerName '${psEscape(containerName)}' -Credential $cred -LibrariesOnly ${libPs}
129
+ `, 300_000);
130
+ return mcpText(result.stdout || "Test toolkit imported.");
131
+ }
132
+ export async function handleImportLicense({ containerName, licenseFile, }) {
133
+ const result = await runPowerShell(`Import-BCDLicense -ContainerName '${psEscape(containerName)}' -LicenseFile '${psEscape(licenseFile)}'`);
134
+ return mcpText(result.stdout || "License imported.");
135
+ }
136
+ export async function handleRunTests({ containerName, testCodeunitId, testFunctionName, appProjectFolder, userName, password, }) {
137
+ const params = [`-ContainerName '${psEscape(containerName)}'`];
138
+ params.push(`-Credential (Get-BCCredential -UserName '${psEscape(userName)}' -Password '${psEscape(password)}')`);
139
+ if (testCodeunitId)
140
+ params.push(`-TestCodeunitId ${testCodeunitId}`);
141
+ if (testFunctionName)
142
+ params.push(`-TestFunctionName '${psEscape(testFunctionName)}'`);
143
+ if (appProjectFolder)
144
+ params.push(`-AppProjectFolder '${psEscape(appProjectFolder)}'`);
145
+ const result = await runPowerShell(`Invoke-BCDTests ${params.join(" ")}`, 600_000);
146
+ return mcpText(result.stdout || "Test run complete.");
147
+ }
package/dist/server.d.ts CHANGED
@@ -1 +1,3 @@
1
- export {};
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare const server: McpServer;
3
+ export declare function startMcpServer(): Promise<void>;
package/dist/server.js CHANGED
@@ -1,42 +1,16 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { z } from "zod";
4
- import { runPowerShell, runRawPowerShell } from "./executor.js";
5
- const server = new McpServer({
6
+ import * as handlers from "./server-handlers.js";
7
+ export const server = new McpServer({
6
8
  name: "bcd",
7
9
  version: "1.0.0",
8
10
  });
9
11
  // ── Container Tools ──────────────────────────────────────
10
- server.tool("list-containers", "List all Business Central Docker containers with their running status", {}, async () => {
11
- const result = await runRawPowerShell(`
12
- Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
13
- $containers = Get-BcContainers
14
- if ($containers.Count -eq 0) {
15
- Write-Output "No BC containers found."
16
- } else {
17
- foreach ($name in $containers) {
18
- $status = docker inspect $name --format '{{.State.Status}}' 2>$null
19
- Write-Output "$name [$status]"
20
- }
21
- }
22
- `);
23
- return { content: [{ type: "text", text: result.stdout || "No containers found." }] };
24
- });
25
- server.tool("container-info", "Get detailed info about a BC container: version, status, and service endpoints", { containerName: z.string().describe("Name of the BC container") }, async ({ containerName }) => {
26
- const result = await runRawPowerShell(`
27
- Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
28
- $bcVersion = Get-BcContainerNavVersion -containerNameOrId '${containerName}'
29
- $webUrl = Get-BcContainerUrl -containerName '${containerName}' -useHttps:$false
30
- $status = docker inspect '${containerName}' --format '{{.State.Status}}' 2>$null
31
- Write-Output "Container : ${containerName}"
32
- Write-Output "Status : $status"
33
- Write-Output "BC Version : $bcVersion"
34
- Write-Output "Web Client : $webUrl"
35
- Write-Output "OData/API : http://${containerName}:7048/BC/api"
36
- Write-Output "Dev Service: http://${containerName}:7049/BC"
37
- `);
38
- return { content: [{ type: "text", text: result.stdout }] };
39
- });
12
+ server.tool("list-containers", "List all Business Central Docker containers with their running status", {}, handlers.handleListContainers);
13
+ server.tool("container-info", "Get detailed info about a BC container: version, status, and service endpoints", { containerName: z.string().describe("Name of the BC container") }, handlers.handleContainerInfo);
40
14
  server.tool("create-container", "Create a new Business Central Docker container with test toolkit. Long-running (5-30 min).", {
41
15
  containerName: z.string().default("bcsandbox").describe("Docker container name"),
42
16
  version: z.string().default("sandbox").describe("BC version: 'sandbox', 'onprem', or specific like '26.0'"),
@@ -48,131 +22,45 @@ server.tool("create-container", "Create a new Business Central Docker container
48
22
  testToolkit: z.enum(["none", "libraries", "full"]).default("libraries").describe("Test toolkit: none, libraries (faster), or full (all MS tests)"),
49
23
  bypassCDN: z.boolean().default(false).describe("Skip Azure CDN, download from blob storage directly"),
50
24
  licenseFile: z.string().optional().describe("Path or URL to .bclicense / .flf license file"),
51
- }, async ({ containerName, version, country, userName, password, memoryLimit, isolation, testToolkit, bypassCDN, licenseFile }) => {
52
- const includeToolkit = testToolkit !== "none" ? "$true" : "$false";
53
- const libOnly = testToolkit === "libraries" ? "$true" : "$false";
54
- const cdn = bypassCDN ? "-BypassCDN" : "";
55
- const lic = licenseFile ? `-LicenseFile '${licenseFile}'` : "";
56
- const result = await runPowerShell(`
57
- New-BCDContainer \`
58
- -ContainerName '${containerName}' \`
59
- -Version '${version}' \`
60
- -Country '${country}' \`
61
- -UserName '${userName}' \`
62
- -Password '${password}' \`
63
- -MemoryLimit '${memoryLimit}' \`
64
- -Isolation '${isolation}' \`
65
- -IncludeTestToolkit ${includeToolkit} \`
66
- -TestLibrariesOnly ${libOnly} \`
67
- ${cdn} ${lic}
68
- `, 1_800_000);
69
- return { content: [{ type: "text", text: result.stdout || "Container creation complete." }] };
70
- });
71
- server.tool("remove-container", "Remove a Business Central Docker container", { containerName: z.string().describe("Name of the container to remove") }, async ({ containerName }) => {
72
- const result = await runPowerShell(`Remove-BCDContainer -ContainerName '${containerName}'`);
73
- return { content: [{ type: "text", text: result.stdout || `Container '${containerName}' removed.` }] };
74
- });
75
- server.tool("start-container", "Start a stopped BC Docker container", { containerName: z.string().describe("Name of the container to start") }, async ({ containerName }) => {
76
- const result = await runPowerShell(`Start-BCDContainer -ContainerName '${containerName}'`);
77
- return { content: [{ type: "text", text: result.stdout || `Container '${containerName}' started.` }] };
78
- });
79
- server.tool("stop-container", "Stop a running BC Docker container", { containerName: z.string().describe("Name of the container to stop") }, async ({ containerName }) => {
80
- const result = await runPowerShell(`Stop-BCDContainer -ContainerName '${containerName}'`);
81
- return { content: [{ type: "text", text: result.stdout || `Container '${containerName}' stopped.` }] };
82
- });
83
- server.tool("restart-container", "Restart a BC Docker container", { containerName: z.string().describe("Name of the container to restart") }, async ({ containerName }) => {
84
- const result = await runPowerShell(`Restart-BCDContainer -ContainerName '${containerName}'`);
85
- return { content: [{ type: "text", text: result.stdout || `Container '${containerName}' restarted.` }] };
86
- });
87
- server.tool("open-webclient", "Open the BC Web Client URL for a container. Returns the URL.", { containerName: z.string().describe("Name of the BC container") }, async ({ containerName }) => {
88
- const result = await runRawPowerShell(`
89
- Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
90
- $url = Get-BcContainerUrl -containerName '${containerName}' -useHttps:$false
91
- Write-Output $url
92
- `);
93
- return { content: [{ type: "text", text: result.stdout || "Could not resolve URL." }] };
94
- });
25
+ }, handlers.handleCreateContainer);
26
+ server.tool("remove-container", "Remove a Business Central Docker container", { containerName: z.string().describe("Name of the container to remove") }, handlers.handleRemoveContainer);
27
+ server.tool("start-container", "Start a stopped BC Docker container", { containerName: z.string().describe("Name of the container to start") }, handlers.handleStartContainer);
28
+ server.tool("stop-container", "Stop a running BC Docker container", { containerName: z.string().describe("Name of the container to stop") }, handlers.handleStopContainer);
29
+ server.tool("restart-container", "Restart a BC Docker container", { containerName: z.string().describe("Name of the container to restart") }, handlers.handleRestartContainer);
30
+ server.tool("open-webclient", "Open the BC Web Client URL for a container. Returns the URL.", { containerName: z.string().describe("Name of the BC container") }, handlers.handleOpenWebclient);
95
31
  // ── App Tools ────────────────────────────────────────────
96
32
  server.tool("list-apps", "List all apps installed in a BC container, optionally filtered by publisher", {
97
33
  containerName: z.string().describe("Target container name"),
98
34
  publisher: z.string().optional().describe("Filter by publisher (e.g. 'Microsoft')"),
99
- }, async ({ containerName, publisher }) => {
100
- const filter = publisher
101
- ? `| Where-Object { $_.Publisher -eq '${publisher}' }`
102
- : "";
103
- const result = await runRawPowerShell(`
104
- Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
105
- $apps = Get-BcContainerAppInfo -containerName '${containerName}' -tenant default -tenantSpecificProperties ${filter}
106
- $apps | Select-Object Name, Publisher, Version, IsInstalled, Scope |
107
- Sort-Object Publisher, Name |
108
- Format-Table -AutoSize | Out-String -Width 200
109
- `);
110
- return { content: [{ type: "text", text: result.stdout || "No apps found." }] };
111
- });
35
+ }, handlers.handleListApps);
112
36
  server.tool("install-app", "Publish and install a .app file into a BC container", {
113
37
  containerName: z.string().describe("Target container name"),
114
38
  appFile: z.string().describe("Full path to the .app file"),
115
39
  userName: z.string().default("admin").describe("Admin username"),
116
40
  password: z.string().default("P@ssw0rd!").describe("Admin password"),
117
- }, async ({ containerName, appFile, userName, password }) => {
118
- const result = await runPowerShell(`
119
- $cred = Get-BCCredential -UserName '${userName}' -Password '${password}'
120
- Install-BCDApp -ContainerName '${containerName}' -AppFile '${appFile}' -Credential $cred
121
- `);
122
- return { content: [{ type: "text", text: result.stdout || "App installed." }] };
123
- });
41
+ }, handlers.handleInstallApp);
124
42
  server.tool("uninstall-app", "Uninstall an app from a BC container by name. Automatically handles dependency order.", {
125
43
  containerName: z.string().describe("Target container name"),
126
44
  appName: z.string().describe("Name of the app to uninstall"),
127
45
  appPublisher: z.string().describe("Publisher of the app"),
128
- }, async ({ containerName, appName, appPublisher }) => {
129
- const result = await runRawPowerShell(`
130
- Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
131
- $allApps = Get-BcContainerAppInfo -containerName '${containerName}' -tenant default -tenantSpecificProperties
132
- $sorted = Get-BcContainerAppInfo -containerName '${containerName}' -tenant default -tenantSpecificProperties -sort DependenciesLast
133
- $target = $allApps | Where-Object { $_.Name -eq '${appName}' -and $_.Publisher -eq '${appPublisher}' }
134
- if (-not $target) { Write-Output "App '${appName}' by '${appPublisher}' not found."; exit }
135
- foreach ($app in $sorted) {
136
- if ($app.Name -eq '${appName}' -and $app.Publisher -eq '${appPublisher}' -and $app.IsInstalled) {
137
- UnInstall-BcContainerApp -name $app.Name -containerName '${containerName}' -publisher $app.Publisher -version $app.Version -force
138
- Write-Output "Uninstalled: $($app.Name) v$($app.Version)"
139
- }
140
- }
141
- `);
142
- return { content: [{ type: "text", text: result.stdout || "Uninstall complete." }] };
143
- });
46
+ }, handlers.handleUninstallApp);
144
47
  server.tool("publish-project", "Compile and publish an AL project folder into a BC container", {
145
48
  containerName: z.string().describe("Target container name"),
146
49
  projectFolder: z.string().describe("Full path to the AL project folder"),
147
50
  userName: z.string().default("admin").describe("Admin username"),
148
51
  password: z.string().default("P@ssw0rd!").describe("Admin password"),
149
- }, async ({ containerName, projectFolder, userName, password }) => {
150
- const result = await runPowerShell(`
151
- $cred = Get-BCCredential -UserName '${userName}' -Password '${password}'
152
- Publish-BCDProject -ContainerName '${containerName}' -ProjectFolder '${projectFolder}' -Credential $cred
153
- `, 300_000);
154
- return { content: [{ type: "text", text: result.stdout || "Project compiled and published." }] };
155
- });
52
+ }, handlers.handlePublishProject);
156
53
  // ── Test & License Tools ─────────────────────────────────
157
54
  server.tool("import-test-toolkit", "Import the BC Test Toolkit (libraries only or full framework) into a container", {
158
55
  containerName: z.string().describe("Target container name"),
159
56
  librariesOnly: z.boolean().default(true).describe("true = just helper libs (faster), false = full MS test codeunits"),
160
57
  userName: z.string().default("admin").describe("Admin username"),
161
58
  password: z.string().default("P@ssw0rd!").describe("Admin password"),
162
- }, async ({ containerName, librariesOnly, userName, password }) => {
163
- const result = await runPowerShell(`
164
- $cred = Get-BCCredential -UserName '${userName}' -Password '${password}'
165
- Import-BCDTestToolkit -ContainerName '${containerName}' -Credential $cred -LibrariesOnly $${librariesOnly}
166
- `, 300_000);
167
- return { content: [{ type: "text", text: result.stdout || "Test toolkit imported." }] };
168
- });
59
+ }, handlers.handleImportTestToolkit);
169
60
  server.tool("import-license", "Import a license file into a BC container", {
170
61
  containerName: z.string().describe("Target container name"),
171
62
  licenseFile: z.string().describe("Full path or URL to .bclicense / .flf file"),
172
- }, async ({ containerName, licenseFile }) => {
173
- const result = await runPowerShell(`Import-BCDLicense -ContainerName '${containerName}' -LicenseFile '${licenseFile}'`);
174
- return { content: [{ type: "text", text: result.stdout || "License imported." }] };
175
- });
63
+ }, handlers.handleImportLicense);
176
64
  server.tool("run-tests", "Run AL tests in a BC container. Can run all tests, a specific codeunit, or compile-and-run from a project folder.", {
177
65
  containerName: z.string().default("bcsandbox").describe("Target container name"),
178
66
  testCodeunitId: z.number().optional().describe("Specific test codeunit ID (omit for all)"),
@@ -180,24 +68,17 @@ server.tool("run-tests", "Run AL tests in a BC container. Can run all tests, a s
180
68
  appProjectFolder: z.string().optional().describe("AL test project folder to compile/publish/run"),
181
69
  userName: z.string().default("admin").describe("Admin username"),
182
70
  password: z.string().default("P@ssw0rd!").describe("Admin password"),
183
- }, async ({ containerName, testCodeunitId, testFunctionName, appProjectFolder, userName, password }) => {
184
- const params = [`-ContainerName '${containerName}'`];
185
- params.push(`-Credential (Get-BCCredential -UserName '${userName}' -Password '${password}')`);
186
- if (testCodeunitId)
187
- params.push(`-TestCodeunitId ${testCodeunitId}`);
188
- if (testFunctionName)
189
- params.push(`-TestFunctionName '${testFunctionName}'`);
190
- if (appProjectFolder)
191
- params.push(`-AppProjectFolder '${appProjectFolder}'`);
192
- const result = await runPowerShell(`Invoke-BCDTests ${params.join(" ")}`, 600_000);
193
- return { content: [{ type: "text", text: result.stdout || "Test run complete." }] };
194
- });
71
+ }, handlers.handleRunTests);
195
72
  // ── Start ────────────────────────────────────────────────
196
- async function main() {
73
+ export async function startMcpServer() {
197
74
  const transport = new StdioServerTransport();
198
75
  await server.connect(transport);
199
76
  }
200
- main().catch((err) => {
201
- console.error("MCP server failed:", err);
202
- process.exit(1);
203
- });
77
+ const isMainModule = process.argv[1] &&
78
+ resolve(fileURLToPath(import.meta.url)) === resolve(process.argv[1]);
79
+ if (isMainModule) {
80
+ startMcpServer().catch((err) => {
81
+ console.error("MCP server failed:", err);
82
+ process.exit(1);
83
+ });
84
+ }
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "bcdocker",
3
- "version": "1.0.0",
4
- "description": "MCP server and CLI for Business Central Docker container management. Create, manage, and test BC containers from your terminal or AI assistant.",
3
+ "version": "1.0.1",
4
+ "description": "Node.js MCP server and CLI that drive the BCDocker Toolkit PowerShell module for AI assistants and scripts.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "bcd": "./dist/cli.js"
7
+ "bcd": "dist/cli-main.js"
8
8
  },
9
9
  "main": "./dist/server.js",
10
10
  "files": [
11
- "dist",
11
+ "dist/*.js",
12
+ "dist/*.d.ts",
12
13
  "ps",
13
14
  "README.md",
14
15
  "LICENSE"
@@ -17,6 +18,8 @@
17
18
  "build": "tsc",
18
19
  "start": "node dist/server.js",
19
20
  "cli": "node dist/cli.js",
21
+ "test": "vitest run",
22
+ "test:coverage": "vitest run --coverage",
20
23
  "prepublishOnly": "npm run build"
21
24
  },
22
25
  "keywords": [
@@ -31,7 +34,7 @@
31
34
  "devops",
32
35
  "cli"
33
36
  ],
34
- "author": "Oleksandr (Ciellos)",
37
+ "author": "olederkach",
35
38
  "license": "MIT",
36
39
  "repository": {
37
40
  "type": "git",
@@ -54,6 +57,8 @@
54
57
  },
55
58
  "devDependencies": {
56
59
  "@types/node": "^22.0.0",
57
- "typescript": "^5.7.0"
60
+ "@vitest/coverage-v8": "^3.0.0",
61
+ "typescript": "^5.7.0",
62
+ "vitest": "^3.0.0"
58
63
  }
59
64
  }