diffler-mcp 0.2.2 → 0.2.4
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/index.js +70 -35
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// stdio↔HTTP MCP proxy
|
|
3
|
-
//
|
|
4
|
-
// diffler TUI. diffler's MCP serves the live review state, so the proxy never
|
|
5
|
-
// owns state — it only bridges transports.
|
|
2
|
+
// stdio↔HTTP MCP proxy bridging Claude Code to a running diffler TUI, lazily
|
|
3
|
+
// (re)connecting so it survives diffler quitting and restarting on a new port.
|
|
6
4
|
|
|
7
5
|
import { readFileSync } from "node:fs";
|
|
8
6
|
import { join } from "node:path";
|
|
@@ -22,9 +20,8 @@ const DEFAULT_PORT = 8417;
|
|
|
22
20
|
function parseArgs(argv) {
|
|
23
21
|
const opts = {};
|
|
24
22
|
for (let i = 0; i < argv.length; i += 1) {
|
|
25
|
-
const arg = argv[i];
|
|
26
23
|
const take = () => argv[(i += 1)];
|
|
27
|
-
switch (
|
|
24
|
+
switch (argv[i]) {
|
|
28
25
|
case "--url":
|
|
29
26
|
opts.url = take();
|
|
30
27
|
break;
|
|
@@ -38,27 +35,22 @@ function parseArgs(argv) {
|
|
|
38
35
|
opts.repo = take();
|
|
39
36
|
break;
|
|
40
37
|
default:
|
|
41
|
-
// unknown flags are ignored so future diffler args don't break older proxies
|
|
42
38
|
break;
|
|
43
39
|
}
|
|
44
40
|
}
|
|
45
41
|
return opts;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
// The port may differ from the configured one after an ephemeral fallback, so
|
|
49
|
-
// diffler publishes the live endpoint to .diffler/mcp.json in the repo.
|
|
50
44
|
function discoverPort(repo) {
|
|
51
45
|
try {
|
|
52
|
-
const
|
|
53
|
-
const port = JSON.parse(raw).port;
|
|
46
|
+
const port = JSON.parse(readFileSync(join(repo, ".diffler", "mcp.json"), "utf8")).port;
|
|
54
47
|
return typeof port === "number" ? port : undefined;
|
|
55
48
|
} catch {
|
|
56
49
|
return undefined;
|
|
57
50
|
}
|
|
58
51
|
}
|
|
59
52
|
|
|
60
|
-
function resolveUrl() {
|
|
61
|
-
const opts = parseArgs(process.argv.slice(2));
|
|
53
|
+
function resolveUrl(opts) {
|
|
62
54
|
const env = process.env;
|
|
63
55
|
const explicit = opts.url || env.DIFFLER_MCP_URL;
|
|
64
56
|
if (explicit) {
|
|
@@ -66,40 +58,83 @@ function resolveUrl() {
|
|
|
66
58
|
}
|
|
67
59
|
const host = opts.host || env.DIFFLER_MCP_HOST || DEFAULT_HOST;
|
|
68
60
|
const repo = opts.repo || process.cwd();
|
|
69
|
-
const port =
|
|
70
|
-
opts.port || env.DIFFLER_MCP_PORT || discoverPort(repo) || DEFAULT_PORT;
|
|
61
|
+
const port = opts.port || env.DIFFLER_MCP_PORT || discoverPort(repo) || DEFAULT_PORT;
|
|
71
62
|
return `http://${host}:${port}/mcp`;
|
|
72
63
|
}
|
|
73
64
|
|
|
74
65
|
async function main() {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
const client = new Client({ name: "diffler-mcp-proxy", version: "0.1.0" });
|
|
78
|
-
try {
|
|
79
|
-
await client.connect(new StreamableHTTPClientTransport(new URL(url)));
|
|
80
|
-
} catch (err) {
|
|
81
|
-
process.stderr.write(
|
|
82
|
-
`diffler-mcp: cannot reach a diffler MCP server at ${url}\n` +
|
|
83
|
-
`Is diffler running in this repo? (${err.message ?? err})\n`,
|
|
84
|
-
);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
66
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
88
67
|
const server = new Server(
|
|
89
68
|
{ name: "diffler", version: "0.1.0" },
|
|
90
69
|
{ capabilities: { tools: {} } },
|
|
91
70
|
);
|
|
92
|
-
server.setRequestHandler(ListToolsRequestSchema, () => client.listTools());
|
|
93
|
-
server.setRequestHandler(CallToolRequestSchema, (request) =>
|
|
94
|
-
client.callTool(request.params),
|
|
95
|
-
);
|
|
96
71
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
72
|
+
let upstream = null;
|
|
73
|
+
let connecting = null;
|
|
74
|
+
|
|
75
|
+
const connect = async () => {
|
|
76
|
+
const client = new Client({ name: "diffler-mcp-proxy", version: "0.1.0" });
|
|
77
|
+
await client.connect(new StreamableHTTPClientTransport(new URL(resolveUrl(opts))));
|
|
78
|
+
// cache synchronously after the await so a later close can't race ahead of it
|
|
79
|
+
upstream = client;
|
|
80
|
+
const drop = () => {
|
|
81
|
+
if (upstream === client) {
|
|
82
|
+
upstream = null;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
client.onclose = drop;
|
|
86
|
+
client.onerror = drop;
|
|
87
|
+
server.sendToolListChanged?.().catch(() => {});
|
|
88
|
+
return client;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const ensureUpstream = () => {
|
|
92
|
+
if (upstream) {
|
|
93
|
+
return Promise.resolve(upstream);
|
|
94
|
+
}
|
|
95
|
+
if (!connecting) {
|
|
96
|
+
connecting = connect().finally(() => {
|
|
97
|
+
connecting = null;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return connecting;
|
|
100
101
|
};
|
|
101
102
|
|
|
103
|
+
const withUpstream = async (fn) => {
|
|
104
|
+
try {
|
|
105
|
+
return await fn(await ensureUpstream());
|
|
106
|
+
} catch {
|
|
107
|
+
upstream = null;
|
|
108
|
+
return await fn(await ensureUpstream());
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
113
|
+
try {
|
|
114
|
+
return await withUpstream((client) => client.listTools());
|
|
115
|
+
} catch {
|
|
116
|
+
return { tools: [] };
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
120
|
+
try {
|
|
121
|
+
return await withUpstream((client) => client.callTool(request.params));
|
|
122
|
+
} catch (err) {
|
|
123
|
+
upstream = null;
|
|
124
|
+
return {
|
|
125
|
+
isError: true,
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: `diffler isn't reachable — is it running in this repo? (${err.message ?? err})`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
102
136
|
await server.connect(new StdioServerTransport());
|
|
137
|
+
void ensureUpstream().catch(() => {});
|
|
103
138
|
}
|
|
104
139
|
|
|
105
140
|
main().catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diffler-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"claude"
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
|
+
"test": "node --test",
|
|
30
31
|
"prepublishOnly": "node --check index.js"
|
|
31
32
|
},
|
|
32
33
|
"publishConfig": {
|