@willjackson/claude-code-bridge 0.3.0 → 0.5.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/README.md CHANGED
@@ -62,13 +62,15 @@ Claude Code Bridge connects two Claude Code instances running on different machi
62
62
  ### Global Installation (Recommended)
63
63
 
64
64
  ```bash
65
- npm install -g claude-code-bridge
65
+ npm install -g @willjackson/claude-code-bridge
66
66
  ```
67
67
 
68
+ After installation, the `claude-bridge` command will be available globally.
69
+
68
70
  ### Run Without Installing
69
71
 
70
72
  ```bash
71
- npx claude-code-bridge start
73
+ npx @willjackson/claude-code-bridge start
72
74
  ```
73
75
 
74
76
  ### Install from Source
@@ -83,33 +85,53 @@ npm link # Makes 'claude-bridge' available globally
83
85
 
84
86
  ## Quick Start
85
87
 
86
- ### Scenario 1: Mac to Docker Container
88
+ ### Scenario 1: Start Bridge and Launch Claude Code
89
+
90
+ The easiest way to get started - start the bridge and launch Claude Code in one command:
91
+
92
+ **Step 1: Configure the MCP server (one-time setup)**
93
+
94
+ ```bash
95
+ claude mcp add remote-bridge -- npx @willjackson/claude-code-bridge mcp-server --connect ws://localhost:8766
96
+ ```
97
+
98
+ **Step 2: Start bridge and launch Claude Code**
99
+
100
+ ```bash
101
+ claude-bridge start --port 8766 --launch-claude
102
+ ```
103
+
104
+ This starts the bridge daemon in the background and launches Claude Code with access to the remote bridge tools.
105
+
106
+ ### Scenario 2: Mac to Remote Server
87
107
 
88
108
  **Step 1: Start the bridge on your Mac**
89
109
 
90
110
  ```bash
91
- claude-bridge start --port 8766
111
+ claude-bridge start --port 8766 --launch-claude
92
112
  ```
93
113
 
94
- **Step 2: Start the bridge in your container and connect**
114
+ **Step 2: Start the bridge on the remote machine and connect**
95
115
 
96
116
  ```bash
97
- # Inside the container (Docksal, DDEV, or Docker)
98
- claude-bridge start --port 8765 --connect ws://host.docker.internal:8766
117
+ # On remote machine (replace YOUR_MAC_IP with actual IP)
118
+ claude-bridge start --with-handlers --connect ws://YOUR_MAC_IP:8766
99
119
  ```
100
120
 
101
- ### Scenario 2: Peer-to-Peer Mode
121
+ Now Claude Code on your Mac can read/write files on the remote machine.
122
+
123
+ ### Scenario 3: Peer-to-Peer Mode
102
124
 
103
125
  Both instances can initiate communication:
104
126
 
105
- **Terminal 1 (Mac):**
127
+ **Machine A:**
106
128
  ```bash
107
129
  claude-bridge start --port 8766
108
130
  ```
109
131
 
110
- **Terminal 2 (Container):**
132
+ **Machine B:**
111
133
  ```bash
112
- claude-bridge start --port 8765 --connect ws://host.docker.internal:8766
134
+ claude-bridge start --port 8765 --connect ws://MACHINE_A_IP:8766
113
135
  ```
114
136
 
115
137
  Once connected, either side can send context or delegate tasks to the other.
@@ -118,17 +140,17 @@ Once connected, either side can send context or delegate tasks to the other.
118
140
 
119
141
  Enable file operations on the remote to allow reading, writing, and deleting files:
120
142
 
121
- **Terminal 1 (Mac - relay mode, no handlers):**
143
+ **Machine A (relay mode, no handlers):**
122
144
  ```bash
123
145
  claude-bridge start --port 8766
124
146
  ```
125
147
 
126
- **Terminal 2 (Container - with file handlers):**
148
+ **Machine B (with file handlers):**
127
149
  ```bash
128
- claude-bridge start --with-handlers --connect ws://host.docker.internal:8766
150
+ claude-bridge start --with-handlers --connect ws://MACHINE_A_IP:8766
129
151
  ```
130
152
 
131
- Now you can read, write, and edit files on the remote container from your Mac.
153
+ Now you can read, write, and edit files on Machine B from Machine A.
132
154
 
133
155
  ## CLI Reference
134
156
 
@@ -157,11 +179,15 @@ Options:
157
179
  -c, --connect <url> URL to connect to on startup (e.g., ws://localhost:8765)
158
180
  -d, --daemon Run in background
159
181
  --with-handlers Enable file operations and task handling (read/write/delete files)
182
+ --launch-claude Start bridge daemon and launch Claude Code
160
183
  ```
161
184
 
162
185
  **Examples:**
163
186
 
164
187
  ```bash
188
+ # Start bridge and launch Claude Code (recommended for local use)
189
+ claude-bridge start --port 8766 --launch-claude
190
+
165
191
  # Start with default settings
166
192
  claude-bridge start
167
193
 
@@ -174,10 +200,66 @@ claude-bridge start --connect ws://192.168.1.100:8765
174
200
  # Start in daemon mode
175
201
  claude-bridge start --daemon
176
202
 
177
- # Start with file operation handlers enabled
178
- claude-bridge start --with-handlers --connect ws://host.docker.internal:8766
203
+ # Start with file operation handlers enabled (for remote machines)
204
+ claude-bridge start --with-handlers --connect ws://192.168.1.100:8766
205
+ ```
206
+
207
+ ### Daemon Mode
208
+
209
+ Daemon mode runs the bridge as a background process, allowing you to close your terminal while the bridge continues running.
210
+
211
+ #### Starting in Daemon Mode
212
+
213
+ ```bash
214
+ # Start bridge in background
215
+ claude-bridge start --daemon
216
+
217
+ # Start with additional options
218
+ claude-bridge start --daemon --port 8766 --with-handlers
219
+
220
+ # Start daemon and connect to remote
221
+ claude-bridge start --daemon --connect ws://192.168.1.100:8765
222
+ ```
223
+
224
+ When started in daemon mode, the bridge:
225
+ - Detaches from the terminal and runs in the background
226
+ - Writes its PID and status to `~/.claude-bridge/status.json`
227
+ - Continues running until explicitly stopped
228
+
229
+ #### Checking Daemon Status
230
+
231
+ ```bash
232
+ claude-bridge status
179
233
  ```
180
234
 
235
+ This shows whether a daemon is running, its PID, listening port, and connected peers.
236
+
237
+ #### Stopping the Daemon
238
+
239
+ ```bash
240
+ claude-bridge stop
241
+ ```
242
+
243
+ This reads the PID from the status file and gracefully shuts down the background process.
244
+
245
+ #### Status File
246
+
247
+ The daemon writes its status to `~/.claude-bridge/status.json`:
248
+
249
+ ```json
250
+ {
251
+ "running": true,
252
+ "pid": 12345,
253
+ "port": 8766,
254
+ "host": "0.0.0.0",
255
+ "instanceName": "bridge-12345",
256
+ "startedAt": "2024-01-15T10:30:00.000Z",
257
+ "peers": []
258
+ }
259
+ ```
260
+
261
+ This file is automatically removed when the daemon stops cleanly.
262
+
181
263
  #### `stop` - Stop the Running Bridge
182
264
 
183
265
  ```bash
@@ -213,7 +295,7 @@ Arguments:
213
295
 
214
296
  ```bash
215
297
  claude-bridge connect ws://localhost:8765
216
- claude-bridge connect ws://host.docker.internal:8766
298
+ claude-bridge connect ws://192.168.1.100:8766
217
299
  ```
218
300
 
219
301
  #### `info` - Show System Information
@@ -251,7 +333,6 @@ listen:
251
333
  # Connection to remote bridge
252
334
  connect:
253
335
  url: ws://localhost:8765
254
- hostGateway: true # Use host.docker.internal
255
336
 
256
337
  # Context sharing settings
257
338
  contextSharing:
@@ -283,7 +364,6 @@ interaction:
283
364
  | `listen.port` | number | 8765 | Port to listen on |
284
365
  | `listen.host` | string | 0.0.0.0 | Host to bind to |
285
366
  | `connect.url` | string | - | WebSocket URL of remote bridge |
286
- | `connect.hostGateway` | boolean | false | Use `host.docker.internal` |
287
367
  | `contextSharing.autoSync` | boolean | true | Automatically sync context |
288
368
  | `contextSharing.syncInterval` | number | 5000 | Sync interval in ms |
289
369
  | `contextSharing.maxChunkTokens` | number | 4000 | Max tokens per context chunk |
@@ -461,20 +541,6 @@ await bridge.delegateTask({
461
541
 
462
542
  > **Security Note:** All file operations are restricted to the project directory where the bridge is running. Paths outside the project root are rejected.
463
543
 
464
- ### Environment Detection
465
-
466
- ```typescript
467
- import { detectEnvironment, getHostGateway } from 'claude-code-bridge';
468
-
469
- const env = detectEnvironment();
470
- console.log('Environment:', env.type); // 'native', 'docksal', 'ddev', 'lando', 'docker'
471
- console.log('Is Container:', env.isContainer);
472
- console.log('Project Name:', env.projectName);
473
-
474
- const gateway = getHostGateway();
475
- console.log('Host Gateway:', gateway); // 'host.docker.internal' or IP address
476
- ```
477
-
478
544
  ### Context Manager
479
545
 
480
546
  ```typescript
@@ -507,13 +573,13 @@ for (const change of delta.changes) {
507
573
 
508
574
  ### Connection Issues
509
575
 
510
- **Problem:** Cannot connect to bridge from container
576
+ **Problem:** Cannot connect to bridge from remote machine
511
577
 
512
578
  **Solutions:**
513
579
  1. Ensure the host bridge is running: `claude-bridge status`
514
580
  2. Check firewall settings allow the port
515
- 3. Verify `host.docker.internal` resolves: `ping host.docker.internal`
516
- 4. Try using the host IP directly: `claude-bridge connect ws://192.168.1.100:8766`
581
+ 3. Verify the IP address is correct and reachable: `ping MACHINE_IP`
582
+ 4. Try using the full URL: `claude-bridge connect ws://192.168.1.100:8766`
517
583
 
518
584
  **Problem:** Connection keeps dropping
519
585
 
@@ -621,12 +687,6 @@ npm test -- tests/unit/bridge/protocol.test.ts
621
687
  npm test -- --grep "WebSocket"
622
688
  ```
623
689
 
624
- ## Documentation
625
-
626
- - [CLAUDE.md](./CLAUDE.md) - Detailed project specification and architecture
627
- - [AGENTS.md](./AGENTS.md) - Guidelines for AI agents working on this codebase
628
- - [instructions.md](./instructions.md) - Development phases and testing strategy
629
-
630
690
  ## License
631
691
 
632
692
  MIT License - see [LICENSE](./LICENSE) for details.
@@ -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: {
@@ -1717,6 +1717,406 @@ function loadConfigSync(configPath) {
1717
1717
  return { ...DEFAULT_CONFIG };
1718
1718
  }
1719
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
+
1720
2120
  export {
1721
2121
  createLogger,
1722
2122
  createChildLogger,
@@ -1745,6 +2145,10 @@ export {
1745
2145
  DEFAULT_CONFIG,
1746
2146
  mergeConfig,
1747
2147
  loadConfig,
1748
- loadConfigSync
2148
+ loadConfigSync,
2149
+ TOOL_DEFINITIONS,
2150
+ createToolHandlers,
2151
+ BridgeMcpServer,
2152
+ startMcpServer
1749
2153
  };
1750
- //# sourceMappingURL=chunk-LUL3SX2F.js.map
2154
+ //# sourceMappingURL=chunk-MHUQYPTB.js.map