shadowjoin 0.3.0 → 0.3.2

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 +16 -3
  2. package/dist/mcp.js +34 -5
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -17,11 +17,11 @@ npx shadowjoin scan \
17
17
  --right crm_export.csv --right-key account_name
18
18
  ```
19
19
 
20
- [![shadowjoin measuring a country-name join: 205 of 249 matched exactly, which looks fine, while five real matches were being dropped including Vietnam, spelled Viet Nam by the UN](assets/brag.gif)](https://github.com/harsh-chandak/shadowjoin/blob/main/assets/brag.mp4)
20
+ [![shadowjoin measuring a country-name join: 205 of 249 matched exactly, which looks fine, while five real matches were being dropped including Vietnam, spelled Viet Nam by the UN](assets/brag.gif)](https://github.com/harsh-chandak/shadowjoin/blob/main/assets/brag.gif)
21
21
 
22
22
  <sub>Fourteen seconds, no sound. Every figure in it is verbatim CLI output from
23
23
  the committed example below — [higher-resolution
24
- version](https://github.com/harsh-chandak/shadowjoin/blob/main/assets/brag.mp4).</sub>
24
+ version](https://github.com/harsh-chandak/shadowjoin/blob/main/assets/brag.gif).</sub>
25
25
 
26
26
  The progress output above is real: shadowjoin shows what it is doing while it
27
27
  works. All of it is suppressed automatically when stdout is not a terminal, so
@@ -219,6 +219,19 @@ const key = (value) => normalize(value, rules);
219
219
  toSql("company_name", rules); // the same thing as Postgres SQL
220
220
  ```
221
221
 
222
+ ### Claude Code plugin (easiest)
223
+
224
+ One command installs the skill and the MCP server together, and prompts for your
225
+ config path:
226
+
227
+ ```bash
228
+ claude plugin marketplace add harsh-chandak/shadowjoin
229
+ claude plugin install shadowjoin@harsh-chandak
230
+ ```
231
+
232
+ Also listed in the [official MCP registry](https://registry.modelcontextprotocol.io)
233
+ as `io.github.harsh-chandak/shadowjoin`.
234
+
222
235
  ### MCP server
223
236
 
224
237
  Lets an agent measure a join, look at real examples, propose a fix, and then
@@ -230,7 +243,7 @@ not something it writes.
230
243
  "mcpServers": {
231
244
  "shadowjoin": {
232
245
  "command": "npx",
233
- "args": ["-y", "-p", "shadowjoin", "shadowjoin-mcp"],
246
+ "args": ["-y", "-p", "shadowjoin", "-p", "@modelcontextprotocol/sdk", "shadowjoin-mcp"],
234
247
  "env": { "SHADOWJOIN_CONFIG": "/abs/path/to/shadowjoin.config.json" }
235
248
  }
236
249
  }
package/dist/mcp.js CHANGED
@@ -11,14 +11,42 @@
11
11
  import { readFile } from "node:fs/promises";
12
12
  import { resolve } from "node:path";
13
13
  import { randomUUID } from "node:crypto";
14
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
15
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
- import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
14
+ import { createRequire } from "node:module";
17
15
  import { scan } from "./scan.js";
18
16
  import { ALL_RULE_IDS, rulesByIds, toSql } from "./normalize.js";
19
17
  import { rowsFromCsv } from "./adapters/csv.js";
20
18
  import { rowsFromJson } from "./adapters/json.js";
21
- const CONFIG_PATH = resolve(process.env.SHADOWJOIN_CONFIG ?? "shadowjoin.config.json");
19
+ const VERSION = createRequire(import.meta.url)("../package.json").version;
20
+ /**
21
+ * The SDK is an optional peer, so the core library and CLI stay dependency-free.
22
+ * Import it dynamically to turn a missing install into an instruction rather
23
+ * than a module-resolution stack trace, which is what an MCP client shows the
24
+ * user as nothing more than "Connection closed".
25
+ */
26
+ async function loadSdk() {
27
+ try {
28
+ const [{ Server }, { StdioServerTransport }, types] = await Promise.all([
29
+ import("@modelcontextprotocol/sdk/server/index.js"),
30
+ import("@modelcontextprotocol/sdk/server/stdio.js"),
31
+ import("@modelcontextprotocol/sdk/types.js"),
32
+ ]);
33
+ return { Server, StdioServerTransport, ...types };
34
+ }
35
+ catch {
36
+ process.stderr.write("shadowjoin-mcp needs @modelcontextprotocol/sdk, which is an optional peer dependency.\n" +
37
+ "Run it with the SDK provided:\n" +
38
+ " npx -y -p shadowjoin -p @modelcontextprotocol/sdk shadowjoin-mcp\n" +
39
+ "or install it alongside shadowjoin:\n" +
40
+ " npm i shadowjoin @modelcontextprotocol/sdk\n");
41
+ process.exit(1);
42
+ }
43
+ }
44
+ // `||`, not `??`: the Claude Code plugin maps this to a user setting, and an
45
+ // unset setting arrives as an empty string rather than as undefined. `??`
46
+ // let "" through, and resolve("") is the working DIRECTORY — so the default
47
+ // install read a directory as its config and told the user to create a file
48
+ // at a path that was already a folder.
49
+ const CONFIG_PATH = resolve(process.env.SHADOWJOIN_CONFIG?.trim() || "shadowjoin.config.json");
22
50
  async function loadConfig() {
23
51
  try {
24
52
  const parsed = JSON.parse(await readFile(CONFIG_PATH, "utf8"));
@@ -133,7 +161,8 @@ const TOOLS = [
133
161
  },
134
162
  },
135
163
  ];
136
- const server = new Server({ name: "shadowjoin", version: "0.1.0" }, { capabilities: { tools: {} } });
164
+ const { Server, StdioServerTransport, CallToolRequestSchema, ListToolsRequestSchema } = await loadSdk();
165
+ const server = new Server({ name: "shadowjoin", version: VERSION }, { capabilities: { tools: {} } });
137
166
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
138
167
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
139
168
  const { name } = request.params;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadowjoin",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Measure the matches your exact join is silently dropping. Runs a shadow matcher alongside your real join key and reports a verified floor on lost matches, plus the normalization rule that would recover each one.",
5
5
  "keywords": [
6
6
  "entity-resolution",
@@ -45,7 +45,7 @@
45
45
  "scripts": {
46
46
  "build": "tsc",
47
47
  "test": "node --test test/*.test.js",
48
- "prepublishOnly": "npm run build"
48
+ "prepublishOnly": "node scripts/prepublish-guard.mjs && npm run build && npm test"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "@modelcontextprotocol/sdk": ">=1",