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.
- package/README.md +16 -3
- package/dist/mcp.js +34 -5
- 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
|
-
[](https://github.com/harsh-chandak/shadowjoin/blob/main/assets/brag.
|
|
20
|
+
[](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.
|
|
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 {
|
|
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
|
|
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
|
|
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.
|
|
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",
|