diffler-mcp 0.1.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.
Files changed (3) hide show
  1. package/README.md +66 -0
  2. package/index.js +108 -0
  3. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # diffler-mcp
2
+
3
+ A tiny stdio↔HTTP bridge that lets Claude Code (or any stdio MCP client) talk to
4
+ the MCP server embedded in a running [diffler](https://github.com/matheusfillipe/diffler)
5
+ review session.
6
+
7
+ diffler's MCP server runs **inside the TUI** as a streamable-HTTP endpoint
8
+ (`http://127.0.0.1:8417/mcp` by default) because it serves the live review state
9
+ on the app's main loop. This proxy is spawned by Claude over stdio and forwards
10
+ every tool call to that endpoint — it owns no state itself.
11
+
12
+ ## Use it with Claude Code
13
+
14
+ Run diffler in your repo (it prints the connect hint and writes
15
+ `.diffler/mcp.json` with the live port), then:
16
+
17
+ ```bash
18
+ claude mcp add diffler -- npx -y diffler-mcp
19
+ ```
20
+
21
+ Or in a checked-in `.mcp.json`:
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "diffler": {
27
+ "command": "npx",
28
+ "args": ["-y", "diffler-mcp"]
29
+ }
30
+ }
31
+ }
32
+ ```
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.
36
+
37
+ ## Configuration
38
+
39
+ Resolution order (first match wins):
40
+
41
+ 1. `--url <url>` / `DIFFLER_MCP_URL` — full endpoint, e.g. `http://127.0.0.1:8417/mcp`
42
+ 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`
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "diffler": {
50
+ "command": "npx",
51
+ "args": ["-y", "diffler-mcp", "--port", "8417"],
52
+ "env": { "DIFFLER_MCP_HOST": "127.0.0.1" }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## Prefer HTTP directly?
59
+
60
+ Claude Code speaks HTTP natively, so you can skip this proxy entirely:
61
+
62
+ ```bash
63
+ claude mcp add --transport http diffler http://127.0.0.1:8417/mcp
64
+ ```
65
+
66
+ The proxy exists for the `npx`, zero-config, auto-port-discovery ergonomics.
package/index.js ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ // stdio↔HTTP MCP proxy: Claude Code spawns this over stdio, and it forwards
3
+ // every tool call to the streamable-HTTP MCP server embedded in a running
4
+ // diffler TUI. diffler's MCP serves the live review state, so the proxy never
5
+ // owns state — it only bridges transports.
6
+
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
11
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
12
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
+ import {
15
+ CallToolRequestSchema,
16
+ ListToolsRequestSchema,
17
+ } from "@modelcontextprotocol/sdk/types.js";
18
+
19
+ const DEFAULT_HOST = "127.0.0.1";
20
+ const DEFAULT_PORT = 8417;
21
+
22
+ function parseArgs(argv) {
23
+ const opts = {};
24
+ for (let i = 0; i < argv.length; i += 1) {
25
+ const arg = argv[i];
26
+ const take = () => argv[(i += 1)];
27
+ switch (arg) {
28
+ case "--url":
29
+ opts.url = take();
30
+ break;
31
+ case "--host":
32
+ opts.host = take();
33
+ break;
34
+ case "--port":
35
+ opts.port = take();
36
+ break;
37
+ case "--repo":
38
+ opts.repo = take();
39
+ break;
40
+ default:
41
+ // unknown flags are ignored so future diffler args don't break older proxies
42
+ break;
43
+ }
44
+ }
45
+ return opts;
46
+ }
47
+
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
+ function discoverPort(repo) {
51
+ try {
52
+ const raw = readFileSync(join(repo, ".diffler", "mcp.json"), "utf8");
53
+ const port = JSON.parse(raw).port;
54
+ return typeof port === "number" ? port : undefined;
55
+ } catch {
56
+ return undefined;
57
+ }
58
+ }
59
+
60
+ function resolveUrl() {
61
+ const opts = parseArgs(process.argv.slice(2));
62
+ const env = process.env;
63
+ const explicit = opts.url || env.DIFFLER_MCP_URL;
64
+ if (explicit) {
65
+ return explicit;
66
+ }
67
+ const host = opts.host || env.DIFFLER_MCP_HOST || DEFAULT_HOST;
68
+ const repo = opts.repo || process.cwd();
69
+ const port =
70
+ opts.port || env.DIFFLER_MCP_PORT || discoverPort(repo) || DEFAULT_PORT;
71
+ return `http://${host}:${port}/mcp`;
72
+ }
73
+
74
+ async function main() {
75
+ const url = resolveUrl();
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
+
88
+ const server = new Server(
89
+ { name: "diffler", version: "0.1.0" },
90
+ { capabilities: { tools: {} } },
91
+ );
92
+ server.setRequestHandler(ListToolsRequestSchema, () => client.listTools());
93
+ server.setRequestHandler(CallToolRequestSchema, (request) =>
94
+ client.callTool(request.params),
95
+ );
96
+
97
+ // when diffler exits the HTTP side drops; tear down so Claude sees the close
98
+ client.onclose = () => {
99
+ void server.close().finally(() => process.exit(0));
100
+ };
101
+
102
+ await server.connect(new StdioServerTransport());
103
+ }
104
+
105
+ main().catch((err) => {
106
+ process.stderr.write(`diffler-mcp: ${err.stack ?? err}\n`);
107
+ process.exit(1);
108
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "diffler-mcp",
3
+ "version": "0.1.0",
4
+ "description": "stdio↔HTTP MCP proxy bridging Claude Code (or any stdio MCP client) to a running diffler review session",
5
+ "type": "module",
6
+ "bin": {
7
+ "diffler-mcp": "./index.js"
8
+ },
9
+ "files": [
10
+ "index.js",
11
+ "README.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "license": "MIT OR Apache-2.0",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/matheusfillipe/diffler.git",
20
+ "directory": "npm/diffler-mcp"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "diffler",
26
+ "code-review",
27
+ "claude"
28
+ ],
29
+ "scripts": {
30
+ "prepublishOnly": "node --check index.js"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.0.0"
37
+ }
38
+ }