@willjackson/claude-code-bridge 0.2.4 → 0.4.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.
@@ -1,7 +1,7 @@
1
1
  // src/utils/logger.ts
2
2
  import pino from "pino";
3
- function isDevelopment() {
4
- return process.env.NODE_ENV !== "production";
3
+ function usePrettyPrint() {
4
+ return process.env.NODE_ENV === "development";
5
5
  }
6
6
  function getDefaultLevel() {
7
7
  const envLevel = process.env.LOG_LEVEL?.toLowerCase();
@@ -17,7 +17,7 @@ function createLogger(name, level) {
17
17
  name,
18
18
  level: logLevel
19
19
  };
20
- if (isDevelopment()) {
20
+ if (usePrettyPrint()) {
21
21
  options.transport = {
22
22
  target: "pino-pretty",
23
23
  options: {
@@ -1613,350 +1613,6 @@ var Bridge = class {
1613
1613
  }
1614
1614
  };
1615
1615
 
1616
- // src/transport/discovery.ts
1617
- import { execSync } from "child_process";
1618
- var logger3 = createLogger("transport:discovery");
1619
- function parseDockerLabels(labels) {
1620
- if (!labels) {
1621
- return null;
1622
- }
1623
- const labelMap = {};
1624
- for (const label of labels.split(",")) {
1625
- const [key, value] = label.split("=");
1626
- if (key && value) {
1627
- labelMap[key.trim()] = value.trim();
1628
- }
1629
- }
1630
- if (labelMap["claude.bridge.enabled"] !== "true") {
1631
- return null;
1632
- }
1633
- return {
1634
- enabled: true,
1635
- port: labelMap["claude.bridge.port"] ? parseInt(labelMap["claude.bridge.port"], 10) : void 0,
1636
- name: labelMap["claude.bridge.name"]
1637
- };
1638
- }
1639
- function isDockerAvailable() {
1640
- try {
1641
- execSync("docker info", { stdio: "pipe", timeout: 5e3 });
1642
- return true;
1643
- } catch {
1644
- return false;
1645
- }
1646
- }
1647
- function discoverDockerPeers() {
1648
- const peers = [];
1649
- if (!isDockerAvailable()) {
1650
- logger3.debug("Docker is not available");
1651
- return peers;
1652
- }
1653
- try {
1654
- const output = execSync(
1655
- 'docker ps --format "{{.ID}}|{{.Names}}|{{.Labels}}|{{.Status}}|{{.Ports}}"',
1656
- {
1657
- encoding: "utf-8",
1658
- stdio: ["pipe", "pipe", "pipe"],
1659
- timeout: 1e4
1660
- }
1661
- );
1662
- const lines = output.trim().split("\n").filter(Boolean);
1663
- for (const line of lines) {
1664
- const [id, names, labels, status, ports] = line.split("|");
1665
- if (!id || !names) continue;
1666
- const bridgeConfig = parseDockerLabels(labels);
1667
- if (!bridgeConfig) continue;
1668
- let port = bridgeConfig.port;
1669
- if (!port && ports) {
1670
- const portMatch = ports.match(/0\.0\.0\.0:(\d+)/);
1671
- if (portMatch) {
1672
- port = parseInt(portMatch[1], 10);
1673
- }
1674
- }
1675
- if (port) {
1676
- peers.push({
1677
- name: bridgeConfig.name || names,
1678
- source: "docker",
1679
- url: `ws://localhost:${port}`,
1680
- containerId: id,
1681
- status
1682
- });
1683
- logger3.debug(
1684
- { containerId: id, name: bridgeConfig.name || names, port },
1685
- "Found bridge-enabled container"
1686
- );
1687
- }
1688
- }
1689
- } catch (error) {
1690
- logger3.debug(
1691
- { error: error.message },
1692
- "Failed to discover Docker peers"
1693
- );
1694
- }
1695
- return peers;
1696
- }
1697
- function discoverDocksalProjects() {
1698
- const peers = [];
1699
- try {
1700
- execSync("which fin", { stdio: "pipe" });
1701
- const output = execSync('fin project list 2>/dev/null || echo ""', {
1702
- encoding: "utf-8",
1703
- stdio: ["pipe", "pipe", "pipe"],
1704
- timeout: 1e4
1705
- });
1706
- const lines = output.trim().split("\n").filter(Boolean);
1707
- for (const line of lines) {
1708
- if (line.includes("NAME") || line.startsWith("-")) continue;
1709
- const parts = line.trim().split(/\s+/);
1710
- const projectName = parts[0];
1711
- const status = parts.slice(1).join(" ");
1712
- if (projectName && status.toLowerCase().includes("running")) {
1713
- peers.push({
1714
- name: projectName,
1715
- source: "docksal",
1716
- url: `ws://${projectName}.docksal:8765`,
1717
- status: "running"
1718
- });
1719
- logger3.debug({ projectName }, "Found Docksal project");
1720
- }
1721
- }
1722
- } catch (error) {
1723
- logger3.debug(
1724
- { error: error.message },
1725
- "Docksal not available or no projects found"
1726
- );
1727
- }
1728
- return peers;
1729
- }
1730
- function discoverDdevProjects() {
1731
- const peers = [];
1732
- try {
1733
- execSync("which ddev", { stdio: "pipe" });
1734
- const output = execSync('ddev list --json-output 2>/dev/null || echo "[]"', {
1735
- encoding: "utf-8",
1736
- stdio: ["pipe", "pipe", "pipe"],
1737
- timeout: 1e4
1738
- });
1739
- try {
1740
- const data = JSON.parse(output);
1741
- const projects = data.raw || [];
1742
- for (const project of projects) {
1743
- if (project.status === "running") {
1744
- peers.push({
1745
- name: project.name,
1746
- source: "ddev",
1747
- url: `ws://${project.name}.ddev.site:8765`,
1748
- status: "running"
1749
- });
1750
- logger3.debug({ projectName: project.name }, "Found DDEV project");
1751
- }
1752
- }
1753
- } catch {
1754
- logger3.debug("Failed to parse DDEV JSON output, trying text parsing");
1755
- const lines = output.trim().split("\n").filter(Boolean);
1756
- for (const line of lines) {
1757
- if (line.includes("running")) {
1758
- const parts = line.trim().split(/\s+/);
1759
- if (parts[0]) {
1760
- peers.push({
1761
- name: parts[0],
1762
- source: "ddev",
1763
- url: `ws://${parts[0]}.ddev.site:8765`,
1764
- status: "running"
1765
- });
1766
- }
1767
- }
1768
- }
1769
- }
1770
- } catch (error) {
1771
- logger3.debug(
1772
- { error: error.message },
1773
- "DDEV not available or no projects found"
1774
- );
1775
- }
1776
- return peers;
1777
- }
1778
- function discoverLandoProjects() {
1779
- const peers = [];
1780
- try {
1781
- execSync("which lando", { stdio: "pipe" });
1782
- const output = execSync('lando list --format json 2>/dev/null || echo "[]"', {
1783
- encoding: "utf-8",
1784
- stdio: ["pipe", "pipe", "pipe"],
1785
- timeout: 1e4
1786
- });
1787
- try {
1788
- const apps = JSON.parse(output);
1789
- for (const app of apps) {
1790
- if (app.running) {
1791
- peers.push({
1792
- name: app.app || app.name,
1793
- source: "lando",
1794
- url: `ws://localhost:8765`,
1795
- // Would need to check app config for actual port
1796
- status: "running"
1797
- });
1798
- logger3.debug({ appName: app.app || app.name }, "Found Lando app");
1799
- }
1800
- }
1801
- } catch {
1802
- logger3.debug("Failed to parse Lando JSON output");
1803
- }
1804
- } catch (error) {
1805
- logger3.debug(
1806
- { error: error.message },
1807
- "Lando not available or no projects found"
1808
- );
1809
- }
1810
- return peers;
1811
- }
1812
- function discoverAllPeers() {
1813
- const allPeers = [];
1814
- const dockerPeers = discoverDockerPeers();
1815
- allPeers.push(...dockerPeers);
1816
- logger3.debug({ count: dockerPeers.length }, "Docker peers discovered");
1817
- const docksalPeers = discoverDocksalProjects();
1818
- allPeers.push(...docksalPeers);
1819
- logger3.debug({ count: docksalPeers.length }, "Docksal peers discovered");
1820
- const ddevPeers = discoverDdevProjects();
1821
- allPeers.push(...ddevPeers);
1822
- logger3.debug({ count: ddevPeers.length }, "DDEV peers discovered");
1823
- const landoPeers = discoverLandoProjects();
1824
- allPeers.push(...landoPeers);
1825
- logger3.debug({ count: landoPeers.length }, "Lando peers discovered");
1826
- return allPeers;
1827
- }
1828
-
1829
- // src/environment/detect.ts
1830
- import { existsSync as existsSync2 } from "fs";
1831
- function isInDocker() {
1832
- return existsSync2("/.dockerenv");
1833
- }
1834
- function isDocksalEnvironment() {
1835
- return !!process.env.DOCKSAL_STACK;
1836
- }
1837
- function isDdevEnvironment() {
1838
- return process.env.IS_DDEV_PROJECT === "true";
1839
- }
1840
- function isLandoEnvironment() {
1841
- return process.env.LANDO === "ON";
1842
- }
1843
- function isDockerComposeEnvironment() {
1844
- return !!process.env.COMPOSE_PROJECT_NAME;
1845
- }
1846
- function detectEnvironment() {
1847
- const platform = process.platform;
1848
- const inDocker = isInDocker();
1849
- if (isDocksalEnvironment()) {
1850
- return {
1851
- type: "docksal",
1852
- isContainer: true,
1853
- projectName: process.env.DOCKSAL_PROJECT,
1854
- projectRoot: process.env.PROJECT_ROOT,
1855
- hostGateway: getHostGateway(),
1856
- platform,
1857
- meta: {
1858
- stack: process.env.DOCKSAL_STACK || "",
1859
- environment: process.env.DOCKSAL_ENVIRONMENT || ""
1860
- }
1861
- };
1862
- }
1863
- if (isDdevEnvironment()) {
1864
- return {
1865
- type: "ddev",
1866
- isContainer: true,
1867
- projectName: process.env.DDEV_PROJECT,
1868
- projectRoot: process.env.DDEV_DOCROOT,
1869
- hostGateway: getHostGateway(),
1870
- platform,
1871
- meta: {
1872
- hostname: process.env.DDEV_HOSTNAME || "",
1873
- primaryUrl: process.env.DDEV_PRIMARY_URL || ""
1874
- }
1875
- };
1876
- }
1877
- if (isLandoEnvironment()) {
1878
- return {
1879
- type: "lando",
1880
- isContainer: true,
1881
- projectName: process.env.LANDO_APP_NAME,
1882
- projectRoot: process.env.LANDO_APP_ROOT,
1883
- hostGateway: getHostGateway(),
1884
- platform,
1885
- meta: {
1886
- service: process.env.LANDO_SERVICE_NAME || "",
1887
- type: process.env.LANDO_SERVICE_TYPE || ""
1888
- }
1889
- };
1890
- }
1891
- if (isDockerComposeEnvironment() && inDocker) {
1892
- return {
1893
- type: "docker-compose",
1894
- isContainer: true,
1895
- projectName: process.env.COMPOSE_PROJECT_NAME,
1896
- hostGateway: getHostGateway(),
1897
- platform,
1898
- meta: {
1899
- service: process.env.COMPOSE_SERVICE || ""
1900
- }
1901
- };
1902
- }
1903
- if (inDocker) {
1904
- return {
1905
- type: "docker",
1906
- isContainer: true,
1907
- hostGateway: getHostGateway(),
1908
- platform
1909
- };
1910
- }
1911
- return {
1912
- type: "native",
1913
- isContainer: false,
1914
- platform
1915
- };
1916
- }
1917
- function getHostGateway() {
1918
- if (process.env.DOCKER_HOST_GATEWAY) {
1919
- return process.env.DOCKER_HOST_GATEWAY;
1920
- }
1921
- if (process.platform === "darwin" || process.platform === "win32") {
1922
- return "host.docker.internal";
1923
- }
1924
- if (isInDocker()) {
1925
- return "host.docker.internal";
1926
- }
1927
- return "localhost";
1928
- }
1929
- function getDefaultConfig(env) {
1930
- const baseConfig = {
1931
- mode: "peer",
1932
- instanceName: env.projectName || `${env.type}-instance`
1933
- };
1934
- if (env.isContainer) {
1935
- return {
1936
- ...baseConfig,
1937
- listen: {
1938
- port: 8765,
1939
- host: "0.0.0.0"
1940
- },
1941
- connect: {
1942
- url: `ws://${env.hostGateway || "host.docker.internal"}:8766`,
1943
- hostGateway: true
1944
- }
1945
- };
1946
- }
1947
- return {
1948
- ...baseConfig,
1949
- listen: {
1950
- port: 8766,
1951
- host: "0.0.0.0"
1952
- },
1953
- connect: {
1954
- url: "ws://localhost:8765",
1955
- hostGateway: false
1956
- }
1957
- };
1958
- }
1959
-
1960
1616
  // src/utils/config.ts
1961
1617
  import * as fs2 from "fs";
1962
1618
  import * as path2 from "path";
@@ -2061,6 +1717,406 @@ function loadConfigSync(configPath) {
2061
1717
  return { ...DEFAULT_CONFIG };
2062
1718
  }
2063
1719
 
1720
+ // src/mcp/tools.ts
1721
+ import { z as z2 } from "zod";
1722
+ var logger3 = createLogger("mcp:tools");
1723
+ var ReadFileInputSchema = z2.object({
1724
+ path: z2.string().describe("Path to the file to read")
1725
+ });
1726
+ var WriteFileInputSchema = z2.object({
1727
+ path: z2.string().describe("Path to the file to write"),
1728
+ content: z2.string().describe("Content to write to the file")
1729
+ });
1730
+ var DeleteFileInputSchema = z2.object({
1731
+ path: z2.string().describe("Path to the file to delete")
1732
+ });
1733
+ var ListDirectoryInputSchema = z2.object({
1734
+ path: z2.string().describe("Path to the directory to list")
1735
+ });
1736
+ var DelegateTaskInputSchema = z2.object({
1737
+ description: z2.string().describe("Description of the task to delegate"),
1738
+ scope: z2.enum(["execute", "analyze", "suggest"]).describe("Task scope"),
1739
+ data: z2.record(z2.unknown()).optional().describe("Additional task data")
1740
+ });
1741
+ var RequestContextInputSchema = z2.object({
1742
+ query: z2.string().describe("Query describing what files to retrieve")
1743
+ });
1744
+ var TOOL_DEFINITIONS = [
1745
+ {
1746
+ name: "bridge_read_file",
1747
+ description: "Read a file from the remote connected instance",
1748
+ inputSchema: ReadFileInputSchema
1749
+ },
1750
+ {
1751
+ name: "bridge_write_file",
1752
+ description: "Write a file to the remote connected instance",
1753
+ inputSchema: WriteFileInputSchema
1754
+ },
1755
+ {
1756
+ name: "bridge_delete_file",
1757
+ description: "Delete a file on the remote connected instance",
1758
+ inputSchema: DeleteFileInputSchema
1759
+ },
1760
+ {
1761
+ name: "bridge_list_directory",
1762
+ description: "List files and folders in a directory on the remote connected instance",
1763
+ inputSchema: ListDirectoryInputSchema
1764
+ },
1765
+ {
1766
+ name: "bridge_delegate_task",
1767
+ description: "Delegate a custom task to the remote connected instance",
1768
+ inputSchema: DelegateTaskInputSchema
1769
+ },
1770
+ {
1771
+ name: "bridge_request_context",
1772
+ description: "Request files matching a query from the remote connected instance",
1773
+ inputSchema: RequestContextInputSchema
1774
+ },
1775
+ {
1776
+ name: "bridge_status",
1777
+ description: "Get bridge status and connected peers",
1778
+ inputSchema: z2.object({})
1779
+ }
1780
+ ];
1781
+ function createToolHandlers(bridge) {
1782
+ const handlers = {};
1783
+ function errorResponse(message) {
1784
+ return {
1785
+ content: [{ type: "text", text: `Error: ${message}` }],
1786
+ isError: true
1787
+ };
1788
+ }
1789
+ function successResponse(text) {
1790
+ return {
1791
+ content: [{ type: "text", text }]
1792
+ };
1793
+ }
1794
+ async function delegateFileTask(action, data, description) {
1795
+ const taskId = `mcp-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1796
+ return bridge.delegateTask({
1797
+ id: taskId,
1798
+ description,
1799
+ scope: "execute",
1800
+ data: { action, ...data }
1801
+ });
1802
+ }
1803
+ handlers["bridge_read_file"] = async (args) => {
1804
+ const input = ReadFileInputSchema.parse(args);
1805
+ logger3.debug({ path: input.path }, "Reading file");
1806
+ try {
1807
+ const result = await delegateFileTask(
1808
+ "read_file",
1809
+ { path: input.path },
1810
+ `Read file: ${input.path}`
1811
+ );
1812
+ if (!result.success) {
1813
+ return errorResponse(result.error || "Failed to read file");
1814
+ }
1815
+ return successResponse(result.data.content);
1816
+ } catch (err) {
1817
+ logger3.error({ error: err.message, path: input.path }, "Failed to read file");
1818
+ return errorResponse(err.message);
1819
+ }
1820
+ };
1821
+ handlers["bridge_write_file"] = async (args) => {
1822
+ const input = WriteFileInputSchema.parse(args);
1823
+ logger3.debug({ path: input.path, contentLength: input.content.length }, "Writing file");
1824
+ try {
1825
+ const result = await delegateFileTask(
1826
+ "write_file",
1827
+ { path: input.path, content: input.content },
1828
+ `Write file: ${input.path}`
1829
+ );
1830
+ if (!result.success) {
1831
+ return errorResponse(result.error || "Failed to write file");
1832
+ }
1833
+ return successResponse(`File written successfully: ${input.path} (${result.data.bytesWritten} bytes)`);
1834
+ } catch (err) {
1835
+ logger3.error({ error: err.message, path: input.path }, "Failed to write file");
1836
+ return errorResponse(err.message);
1837
+ }
1838
+ };
1839
+ handlers["bridge_delete_file"] = async (args) => {
1840
+ const input = DeleteFileInputSchema.parse(args);
1841
+ logger3.debug({ path: input.path }, "Deleting file");
1842
+ try {
1843
+ const result = await delegateFileTask(
1844
+ "delete_file",
1845
+ { path: input.path },
1846
+ `Delete file: ${input.path}`
1847
+ );
1848
+ if (!result.success) {
1849
+ return errorResponse(result.error || "Failed to delete file");
1850
+ }
1851
+ return successResponse(`File deleted successfully: ${input.path}`);
1852
+ } catch (err) {
1853
+ logger3.error({ error: err.message, path: input.path }, "Failed to delete file");
1854
+ return errorResponse(err.message);
1855
+ }
1856
+ };
1857
+ handlers["bridge_list_directory"] = async (args) => {
1858
+ const input = ListDirectoryInputSchema.parse(args);
1859
+ logger3.debug({ path: input.path }, "Listing directory");
1860
+ try {
1861
+ const result = await delegateFileTask(
1862
+ "list_directory",
1863
+ { path: input.path },
1864
+ `List directory: ${input.path}`
1865
+ );
1866
+ if (!result.success) {
1867
+ return errorResponse(result.error || "Failed to list directory");
1868
+ }
1869
+ const entries = result.data.entries;
1870
+ if (!entries || entries.length === 0) {
1871
+ return successResponse(`Directory is empty: ${input.path}`);
1872
+ }
1873
+ const listing = entries.map((e) => `${e.type === "directory" ? "\u{1F4C1}" : "\u{1F4C4}"} ${e.name}`).join("\n");
1874
+ return successResponse(`Contents of ${input.path}:
1875
+ ${listing}`);
1876
+ } catch (err) {
1877
+ logger3.error({ error: err.message, path: input.path }, "Failed to list directory");
1878
+ return errorResponse(err.message);
1879
+ }
1880
+ };
1881
+ handlers["bridge_delegate_task"] = async (args) => {
1882
+ const input = DelegateTaskInputSchema.parse(args);
1883
+ logger3.debug({ description: input.description, scope: input.scope }, "Delegating task");
1884
+ try {
1885
+ const taskId = `mcp-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1886
+ const result = await bridge.delegateTask({
1887
+ id: taskId,
1888
+ description: input.description,
1889
+ scope: input.scope,
1890
+ data: input.data
1891
+ });
1892
+ if (!result.success) {
1893
+ return errorResponse(result.error || "Task failed");
1894
+ }
1895
+ return successResponse(JSON.stringify(result.data, null, 2));
1896
+ } catch (err) {
1897
+ logger3.error({ error: err.message }, "Failed to delegate task");
1898
+ return errorResponse(err.message);
1899
+ }
1900
+ };
1901
+ handlers["bridge_request_context"] = async (args) => {
1902
+ const input = RequestContextInputSchema.parse(args);
1903
+ logger3.debug({ query: input.query }, "Requesting context");
1904
+ try {
1905
+ const files = await bridge.requestContext(input.query);
1906
+ if (files.length === 0) {
1907
+ return successResponse("No files found matching the query.");
1908
+ }
1909
+ const fileResults = files.map((f) => {
1910
+ const header = `=== ${f.path} ===`;
1911
+ const content = f.content;
1912
+ return `${header}
1913
+ ${content}`;
1914
+ }).join("\n\n");
1915
+ return successResponse(`Found ${files.length} file(s):
1916
+
1917
+ ${fileResults}`);
1918
+ } catch (err) {
1919
+ logger3.error({ error: err.message }, "Failed to request context");
1920
+ return errorResponse(err.message);
1921
+ }
1922
+ };
1923
+ handlers["bridge_status"] = async () => {
1924
+ logger3.debug("Getting bridge status");
1925
+ const peers = bridge.getPeers();
1926
+ const peerCount = bridge.getPeerCount();
1927
+ const isStarted = bridge.isStarted();
1928
+ const mode = bridge.getMode();
1929
+ const instanceName = bridge.getInstanceName();
1930
+ const status = {
1931
+ instanceName,
1932
+ mode,
1933
+ started: isStarted,
1934
+ peerCount,
1935
+ peers: peers.map((p) => ({
1936
+ id: p.id,
1937
+ name: p.name,
1938
+ connectedAt: new Date(p.connectedAt).toISOString(),
1939
+ lastActivity: new Date(p.lastActivity).toISOString()
1940
+ }))
1941
+ };
1942
+ return successResponse(JSON.stringify(status, null, 2));
1943
+ };
1944
+ return handlers;
1945
+ }
1946
+ function zodToJsonSchema(schema) {
1947
+ if (schema instanceof z2.ZodObject) {
1948
+ const shape = schema.shape;
1949
+ const properties = {};
1950
+ const required = [];
1951
+ for (const [key, value] of Object.entries(shape)) {
1952
+ const zodValue = value;
1953
+ properties[key] = zodToJsonSchema(zodValue);
1954
+ if (!(zodValue instanceof z2.ZodOptional)) {
1955
+ required.push(key);
1956
+ }
1957
+ }
1958
+ return {
1959
+ type: "object",
1960
+ properties,
1961
+ ...required.length > 0 ? { required } : {}
1962
+ };
1963
+ }
1964
+ if (schema instanceof z2.ZodString) {
1965
+ const result = { type: "string" };
1966
+ if (schema.description) {
1967
+ result.description = schema.description;
1968
+ }
1969
+ return result;
1970
+ }
1971
+ if (schema instanceof z2.ZodEnum) {
1972
+ return {
1973
+ type: "string",
1974
+ enum: schema.options
1975
+ };
1976
+ }
1977
+ if (schema instanceof z2.ZodOptional) {
1978
+ return zodToJsonSchema(schema.unwrap());
1979
+ }
1980
+ if (schema instanceof z2.ZodRecord) {
1981
+ return {
1982
+ type: "object",
1983
+ additionalProperties: true
1984
+ };
1985
+ }
1986
+ if (schema instanceof z2.ZodUnknown) {
1987
+ return {};
1988
+ }
1989
+ return { type: "string" };
1990
+ }
1991
+
1992
+ // src/mcp/server.ts
1993
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1994
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1995
+ import {
1996
+ CallToolRequestSchema,
1997
+ ListToolsRequestSchema
1998
+ } from "@modelcontextprotocol/sdk/types.js";
1999
+ var logger4 = createLogger("mcp:server");
2000
+ var BridgeMcpServer = class {
2001
+ server;
2002
+ bridge;
2003
+ config;
2004
+ toolHandlers = null;
2005
+ constructor(config) {
2006
+ this.config = config;
2007
+ this.server = new Server(
2008
+ {
2009
+ name: config.name ?? "claude-bridge",
2010
+ version: config.version ?? "0.4.0"
2011
+ },
2012
+ {
2013
+ capabilities: {
2014
+ tools: {}
2015
+ }
2016
+ }
2017
+ );
2018
+ const bridgeConfig = {
2019
+ mode: "client",
2020
+ instanceName: config.instanceName ?? `mcp-server-${process.pid}`,
2021
+ connect: {
2022
+ url: config.bridgeUrl
2023
+ },
2024
+ taskTimeout: config.taskTimeout ?? 6e4
2025
+ };
2026
+ this.bridge = new Bridge(bridgeConfig);
2027
+ this.registerHandlers();
2028
+ }
2029
+ /**
2030
+ * Register MCP request handlers
2031
+ */
2032
+ registerHandlers() {
2033
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
2034
+ logger4.debug("Listing tools");
2035
+ return {
2036
+ tools: TOOL_DEFINITIONS.map((tool) => ({
2037
+ name: tool.name,
2038
+ description: tool.description,
2039
+ inputSchema: zodToJsonSchema(tool.inputSchema)
2040
+ }))
2041
+ };
2042
+ });
2043
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
2044
+ const { name, arguments: args } = request.params;
2045
+ logger4.debug({ tool: name }, "Tool call received");
2046
+ if (!this.toolHandlers) {
2047
+ this.toolHandlers = createToolHandlers(this.bridge);
2048
+ }
2049
+ const handler = this.toolHandlers[name];
2050
+ if (!handler) {
2051
+ logger4.warn({ tool: name }, "Unknown tool requested");
2052
+ return {
2053
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
2054
+ isError: true
2055
+ };
2056
+ }
2057
+ try {
2058
+ const result = await handler(args ?? {});
2059
+ logger4.debug({ tool: name, isError: result.isError }, "Tool call completed");
2060
+ return {
2061
+ content: result.content,
2062
+ isError: result.isError
2063
+ };
2064
+ } catch (err) {
2065
+ logger4.error({ tool: name, error: err.message }, "Tool call failed");
2066
+ return {
2067
+ content: [{ type: "text", text: `Error: ${err.message}` }],
2068
+ isError: true
2069
+ };
2070
+ }
2071
+ });
2072
+ }
2073
+ /**
2074
+ * Start the MCP server
2075
+ * Connects to bridge daemon and starts listening on stdio
2076
+ */
2077
+ async start() {
2078
+ console.error("[MCP] Starting bridge MCP server...");
2079
+ console.error(`[MCP] Connecting to bridge at ${this.config.bridgeUrl}`);
2080
+ try {
2081
+ await this.bridge.start();
2082
+ console.error("[MCP] Connected to bridge daemon");
2083
+ this.toolHandlers = createToolHandlers(this.bridge);
2084
+ const transport = new StdioServerTransport();
2085
+ await this.server.connect(transport);
2086
+ console.error("[MCP] MCP server started and listening on stdio");
2087
+ logger4.info("MCP server started");
2088
+ } catch (err) {
2089
+ console.error(`[MCP] Failed to start: ${err.message}`);
2090
+ throw err;
2091
+ }
2092
+ }
2093
+ /**
2094
+ * Stop the MCP server
2095
+ */
2096
+ async stop() {
2097
+ console.error("[MCP] Stopping MCP server...");
2098
+ try {
2099
+ await this.bridge.stop();
2100
+ await this.server.close();
2101
+ console.error("[MCP] MCP server stopped");
2102
+ logger4.info("MCP server stopped");
2103
+ } catch (err) {
2104
+ console.error(`[MCP] Error during shutdown: ${err.message}`);
2105
+ }
2106
+ }
2107
+ /**
2108
+ * Get the bridge instance
2109
+ */
2110
+ getBridge() {
2111
+ return this.bridge;
2112
+ }
2113
+ };
2114
+ async function startMcpServer(config) {
2115
+ const server = new BridgeMcpServer(config);
2116
+ await server.start();
2117
+ return server;
2118
+ }
2119
+
2064
2120
  export {
2065
2121
  createLogger,
2066
2122
  createChildLogger,
@@ -2086,19 +2142,13 @@ export {
2086
2142
  createContextRequestMessage,
2087
2143
  createNotificationMessage,
2088
2144
  Bridge,
2089
- parseDockerLabels,
2090
- isDockerAvailable,
2091
- discoverDockerPeers,
2092
- discoverDocksalProjects,
2093
- discoverDdevProjects,
2094
- discoverLandoProjects,
2095
- discoverAllPeers,
2096
- detectEnvironment,
2097
- getHostGateway,
2098
- getDefaultConfig,
2099
2145
  DEFAULT_CONFIG,
2100
2146
  mergeConfig,
2101
2147
  loadConfig,
2102
- loadConfigSync
2148
+ loadConfigSync,
2149
+ TOOL_DEFINITIONS,
2150
+ createToolHandlers,
2151
+ BridgeMcpServer,
2152
+ startMcpServer
2103
2153
  };
2104
- //# sourceMappingURL=chunk-5HWFDZKH.js.map
2154
+ //# sourceMappingURL=chunk-MHUQYPTB.js.map