diffler-mcp 0.6.7 → 0.6.9

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 (3) hide show
  1. package/README.md +11 -6
  2. package/index.js +29 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -31,8 +31,9 @@ Or in a checked-in `.mcp.json`:
31
31
  }
32
32
  ```
33
33
 
34
- Run Claude from the repo root and the proxy auto-discovers the port from
35
- `.diffler/mcp.json`. No diffler running ⇒ the proxy exits with a clear error.
34
+ Start Claude anywhere inside the repo and the proxy auto-discovers the port
35
+ from `.diffler/mcp.json`. No diffler running ⇒ every tool call reports which
36
+ directory it searched.
36
37
 
37
38
  ## Configuration
38
39
 
@@ -40,21 +41,25 @@ Resolution order (first match wins):
40
41
 
41
42
  1. `--url <url>` / `DIFFLER_MCP_URL` — full endpoint, e.g. `http://127.0.0.1:8417/mcp`
42
43
  2. `--port <n>` / `DIFFLER_MCP_PORT` and `--host <h>` / `DIFFLER_MCP_HOST`
43
- 3. the live port in `<repo>/.diffler/mcp.json` (`--repo <path>`, default: cwd)
44
- 4. `http://127.0.0.1:8417/mcp`
44
+ 3. the live port in the nearest `.diffler/mcp.json`, searching the start
45
+ directory (`--repo <path>`, default: cwd) and then each parent
46
+
47
+ Discovery covers the normal case, so nothing needs configuring:
45
48
 
46
49
  ```json
47
50
  {
48
51
  "mcpServers": {
49
52
  "diffler": {
50
53
  "command": "npx",
51
- "args": ["-y", "diffler-mcp", "--port", "8417"],
52
- "env": { "DIFFLER_MCP_HOST": "127.0.0.1" }
54
+ "args": ["-y", "diffler-mcp"]
53
55
  }
54
56
  }
55
57
  }
56
58
  ```
57
59
 
60
+ Reach for `--port`, `--host` or `--repo` when diffler runs somewhere the walk-up
61
+ cannot see, such as another machine over a tunnel.
62
+
58
63
  ## Prefer HTTP directly?
59
64
 
60
65
  Claude Code speaks HTTP natively, so you can skip this proxy entirely:
package/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // (re)connecting so it survives diffler quitting and restarting on a new port.
4
4
 
5
5
  import { readFileSync } from "node:fs";
6
- import { join } from "node:path";
6
+ import { dirname, join, resolve } from "node:path";
7
7
 
8
8
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
9
9
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -15,7 +15,7 @@ import {
15
15
  } from "@modelcontextprotocol/sdk/types.js";
16
16
 
17
17
  const DEFAULT_HOST = "127.0.0.1";
18
- const DEFAULT_PORT = 8417;
18
+ const ENDPOINT_FILE = join(".diffler", "mcp.json");
19
19
 
20
20
  function parseArgs(argv) {
21
21
  const opts = {};
@@ -41,12 +41,27 @@ function parseArgs(argv) {
41
41
  return opts;
42
42
  }
43
43
 
44
- function discoverPort(repo) {
45
- try {
46
- const port = JSON.parse(readFileSync(join(repo, ".diffler", "mcp.json"), "utf8")).port;
47
- return typeof port === "number" ? port : undefined;
48
- } catch {
49
- return undefined;
44
+ // Each diffler publishes its live port under its own repo root, so the walk-up
45
+ // finds the instance owning the directory the editor launched from. Falling
46
+ // back to a fixed port would instead attach to whichever repo's diffler happens
47
+ // to hold it and serve that repo's review.
48
+ function discoverPort(from) {
49
+ const start = resolve(from);
50
+ let dir = start;
51
+ for (;;) {
52
+ try {
53
+ const { port } = JSON.parse(readFileSync(join(dir, ENDPOINT_FILE), "utf8"));
54
+ if (typeof port === "number") {
55
+ return port;
56
+ }
57
+ } catch {
58
+ // unreadable or malformed reads like absent: keep walking up
59
+ }
60
+ const parent = dirname(dir);
61
+ if (parent === dir) {
62
+ throw new Error(`no diffler is running in ${start} or any parent (no ${ENDPOINT_FILE})`);
63
+ }
64
+ dir = parent;
50
65
  }
51
66
  }
52
67
 
@@ -57,8 +72,7 @@ function resolveUrl(opts) {
57
72
  return explicit;
58
73
  }
59
74
  const host = opts.host || env.DIFFLER_MCP_HOST || DEFAULT_HOST;
60
- const repo = opts.repo || process.cwd();
61
- const port = opts.port || env.DIFFLER_MCP_PORT || discoverPort(repo) || DEFAULT_PORT;
75
+ const port = opts.port || env.DIFFLER_MCP_PORT || discoverPort(opts.repo || process.cwd());
62
76
  return `http://${host}:${port}/mcp`;
63
77
  }
64
78
 
@@ -134,7 +148,11 @@ async function main() {
134
148
  });
135
149
 
136
150
  await server.connect(new StdioServerTransport());
137
- void ensureUpstream().catch(() => {});
151
+ // stdout is the MCP channel, so the startup diagnosis goes to stderr, where
152
+ // clients surface it; the proxy stays up and reconnects when diffler starts
153
+ void ensureUpstream().catch((err) => {
154
+ process.stderr.write(`diffler-mcp: ${err.message ?? err}\n`);
155
+ });
138
156
  }
139
157
 
140
158
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffler-mcp",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "stdio↔HTTP MCP proxy bridging Claude Code (or any stdio MCP client) to a running diffler review session",
5
5
  "type": "module",
6
6
  "bin": {