kontex-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.
- package/README.md +32 -0
- package/bin/kontex-mcp.js +10 -0
- package/lib/claude-code.js +39 -0
- package/lib/cli.js +203 -0
- package/lib/clients.js +117 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# kontex-mcp
|
|
2
|
+
|
|
3
|
+
One command to connect [Kontex](https://www.kontex.so) to Claude Desktop,
|
|
4
|
+
Claude Code, and Cursor.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npx kontex-mcp connect kx_your_key
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Get a key from the MCP keys page in your Kontex dashboard. Each key belongs
|
|
11
|
+
to one person and can be revoked there at any time.
|
|
12
|
+
|
|
13
|
+
What it does, per tool it finds on your machine:
|
|
14
|
+
|
|
15
|
+
- **Claude Desktop**: adds a `kontex` server to `claude_desktop_config.json`
|
|
16
|
+
that runs `npx mcp-remote` against `https://www.kontex.so/api/mcp` with your
|
|
17
|
+
key in an environment variable. Restart Claude Desktop afterwards.
|
|
18
|
+
- **Claude Code**: runs `claude mcp add --transport http --scope user kontex …`.
|
|
19
|
+
- **Cursor**: adds a `kontex` server with `url` and `headers` to
|
|
20
|
+
`~/.cursor/mcp.json`.
|
|
21
|
+
|
|
22
|
+
Other servers in those files are left untouched; the previous file is kept
|
|
23
|
+
as `.bak`. Tools that are not installed are skipped.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx kontex-mcp connect kx_… --dry-run # show the changes only
|
|
27
|
+
npx kontex-mcp connect kx_… --only cursor # one tool
|
|
28
|
+
npx kontex-mcp disconnect # remove Kontex from all three
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires Node 18 or newer. claude.ai and ChatGPT web connectors are not
|
|
32
|
+
covered: they need OAuth, which Kontex will add separately.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { SERVER_NAME } from "./clients.js";
|
|
4
|
+
|
|
5
|
+
/** Claude Code keeps its own config; we go through its CLI. */
|
|
6
|
+
|
|
7
|
+
const BIN = process.platform === "win32" ? "claude.cmd" : "claude";
|
|
8
|
+
|
|
9
|
+
export function claudeCodeInstalled() {
|
|
10
|
+
const r = spawnSync(BIN, ["--version"], { stdio: "ignore", shell: process.platform === "win32" });
|
|
11
|
+
return !r.error && r.status === 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** @param {{ url: string; key: string }} t */
|
|
15
|
+
export function claudeCodeAddArgs({ url, key }) {
|
|
16
|
+
return ["mcp", "add", "--transport", "http", "--scope", "user", SERVER_NAME, url, "--header", `Authorization: Bearer ${key}`];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function claudeCodeRemoveArgs() {
|
|
20
|
+
return ["mcp", "remove", "--scope", "user", SERVER_NAME];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string[]} args
|
|
25
|
+
* @returns {{ ok: boolean; output: string }}
|
|
26
|
+
*/
|
|
27
|
+
export function runClaude(args) {
|
|
28
|
+
const r = spawnSync(BIN, args, { encoding: "utf8", shell: process.platform === "win32" });
|
|
29
|
+
const output = `${r.stdout ?? ""}${r.stderr ?? ""}`.trim();
|
|
30
|
+
return { ok: !r.error && r.status === 0, output };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Human-readable form for --dry-run; the key is masked.
|
|
35
|
+
* @param {string[]} args
|
|
36
|
+
*/
|
|
37
|
+
export function formatCommand(args) {
|
|
38
|
+
return ["claude", ...args.map((a) => (a.startsWith("Authorization: Bearer ") ? `"Authorization: Bearer kx_…"` : a))].join(" ");
|
|
39
|
+
}
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_URL,
|
|
4
|
+
claudeDesktopConfigPath,
|
|
5
|
+
claudeDesktopEntry,
|
|
6
|
+
cursorConfigPath,
|
|
7
|
+
cursorEntry,
|
|
8
|
+
dirExists,
|
|
9
|
+
looksLikeKey,
|
|
10
|
+
readJson,
|
|
11
|
+
withServer,
|
|
12
|
+
withoutServer,
|
|
13
|
+
writeJson,
|
|
14
|
+
} from "./clients.js";
|
|
15
|
+
import { claudeCodeAddArgs, claudeCodeInstalled, claudeCodeRemoveArgs, formatCommand, runClaude } from "./claude-code.js";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
|
|
18
|
+
const CLIENT_IDS = /** @type {const} */ (["claude-desktop", "claude-code", "cursor"]);
|
|
19
|
+
/** @typedef {(typeof CLIENT_IDS)[number]} ClientId */
|
|
20
|
+
|
|
21
|
+
const LABELS = { "claude-desktop": "Claude Desktop", "claude-code": "Claude Code", cursor: "Cursor" };
|
|
22
|
+
|
|
23
|
+
const HELP = `kontex-mcp — connect Kontex to your AI tools
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
npx kontex-mcp connect <key> Add Kontex to Claude Desktop, Claude Code, and Cursor
|
|
27
|
+
npx kontex-mcp disconnect Remove Kontex from all three
|
|
28
|
+
npx kontex-mcp --help
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--only <list> Comma-separated subset: claude-desktop, claude-code, cursor
|
|
32
|
+
--url <url> MCP endpoint (default: ${DEFAULT_URL})
|
|
33
|
+
--dry-run Show what would change without writing anything
|
|
34
|
+
|
|
35
|
+
Get a key from https://www.kontex.so/dashboard/mcp. Each key belongs to one
|
|
36
|
+
person; do not share it. Tools that are not installed are skipped.`;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string[]} argv
|
|
40
|
+
* @returns {{ command: string | undefined; positional: string[]; only: ClientId[] | null; url: string; dryRun: boolean; help: boolean }}
|
|
41
|
+
*/
|
|
42
|
+
export function parseArgs(argv) {
|
|
43
|
+
/** @type {string[]} */
|
|
44
|
+
const positional = [];
|
|
45
|
+
/** @type {ClientId[] | null} */
|
|
46
|
+
let only = null;
|
|
47
|
+
let url = DEFAULT_URL;
|
|
48
|
+
let dryRun = false;
|
|
49
|
+
let help = false;
|
|
50
|
+
for (let i = 0; i < argv.length; i++) {
|
|
51
|
+
const a = argv[i];
|
|
52
|
+
if (a === "--help" || a === "-h") help = true;
|
|
53
|
+
else if (a === "--dry-run") dryRun = true;
|
|
54
|
+
else if (a === "--url") url = argv[++i] ?? url;
|
|
55
|
+
else if (a.startsWith("--url=")) url = a.slice(6);
|
|
56
|
+
else if (a === "--only" || a.startsWith("--only=")) {
|
|
57
|
+
const raw = a === "--only" ? (argv[++i] ?? "") : a.slice(7);
|
|
58
|
+
const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
59
|
+
const bad = ids.filter((id) => !CLIENT_IDS.includes(/** @type {ClientId} */ (id)));
|
|
60
|
+
if (bad.length) throw new Error(`Unknown client(s): ${bad.join(", ")}. Use: ${CLIENT_IDS.join(", ")}`);
|
|
61
|
+
only = /** @type {ClientId[]} */ (ids);
|
|
62
|
+
} else if (a.startsWith("-")) throw new Error(`Unknown option: ${a}`);
|
|
63
|
+
else positional.push(a);
|
|
64
|
+
}
|
|
65
|
+
const [command, ...rest] = positional;
|
|
66
|
+
return { command, positional: rest, only, url, dryRun, help };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {string[]} argv
|
|
71
|
+
* @param {{ log?: (s: string) => void; home?: string }} [io]
|
|
72
|
+
* @returns {Promise<number>} exit code
|
|
73
|
+
*/
|
|
74
|
+
export async function main(argv, io = {}) {
|
|
75
|
+
const log = io.log ?? ((s) => console.log(s));
|
|
76
|
+
let opts;
|
|
77
|
+
try {
|
|
78
|
+
opts = parseArgs(argv);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
log(e instanceof Error ? e.message : String(e));
|
|
81
|
+
return 2;
|
|
82
|
+
}
|
|
83
|
+
if (opts.help || !opts.command) {
|
|
84
|
+
log(HELP);
|
|
85
|
+
return opts.help ? 0 : 2;
|
|
86
|
+
}
|
|
87
|
+
const wanted = opts.only ?? [...CLIENT_IDS];
|
|
88
|
+
const paths = { desktop: claudeDesktopConfigPath({ home: io.home }), cursor: cursorConfigPath({ home: io.home }) };
|
|
89
|
+
|
|
90
|
+
if (opts.command === "connect") {
|
|
91
|
+
const key = opts.positional[0];
|
|
92
|
+
if (!key) {
|
|
93
|
+
log("Missing key. Usage: npx kontex-mcp connect <key>");
|
|
94
|
+
return 2;
|
|
95
|
+
}
|
|
96
|
+
if (!looksLikeKey(key)) {
|
|
97
|
+
log("That does not look like a Kontex key (they start with kx_). Copy it from the MCP keys page.");
|
|
98
|
+
return 2;
|
|
99
|
+
}
|
|
100
|
+
let url;
|
|
101
|
+
try {
|
|
102
|
+
url = new URL(opts.url).toString();
|
|
103
|
+
} catch {
|
|
104
|
+
log(`Invalid --url: ${opts.url}`);
|
|
105
|
+
return 2;
|
|
106
|
+
}
|
|
107
|
+
const target = { url, key };
|
|
108
|
+
log(opts.dryRun ? "Dry run: nothing will be written." : "Connecting Kontex to your AI tools…");
|
|
109
|
+
let configured = 0;
|
|
110
|
+
/** @type {string[]} */
|
|
111
|
+
const notes = [];
|
|
112
|
+
|
|
113
|
+
if (wanted.includes("claude-desktop")) {
|
|
114
|
+
if (dirExists(path.dirname(paths.desktop))) {
|
|
115
|
+
const next = withServer(readJson(paths.desktop), claudeDesktopEntry(target));
|
|
116
|
+
if (opts.dryRun) log(` would write ${paths.desktop}:\n${indent(JSON.stringify(next.mcpServers.kontex, null, 2))}`);
|
|
117
|
+
else writeJson(paths.desktop, next);
|
|
118
|
+
log(`✓ ${LABELS["claude-desktop"]}`);
|
|
119
|
+
configured++;
|
|
120
|
+
notes.push("Restart Claude Desktop to see Kontex.");
|
|
121
|
+
} else log(`– ${LABELS["claude-desktop"]} (not installed, skipped)`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (wanted.includes("claude-code")) {
|
|
125
|
+
if (claudeCodeInstalled()) {
|
|
126
|
+
const add = claudeCodeAddArgs(target);
|
|
127
|
+
if (opts.dryRun) log(` would run: ${formatCommand(claudeCodeRemoveArgs())} (if present)\n would run: ${formatCommand(add)}`);
|
|
128
|
+
else {
|
|
129
|
+
runClaude(claudeCodeRemoveArgs()); // replace a previous key; failure just means it was not there
|
|
130
|
+
const r = runClaude(add);
|
|
131
|
+
if (!r.ok) {
|
|
132
|
+
log(`✗ ${LABELS["claude-code"]}: ${r.output || "claude mcp add failed"}`);
|
|
133
|
+
return finish(log, configured, notes, 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
log(`✓ ${LABELS["claude-code"]}`);
|
|
137
|
+
configured++;
|
|
138
|
+
} else log(`– ${LABELS["claude-code"]} (not installed, skipped)`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (wanted.includes("cursor")) {
|
|
142
|
+
if (dirExists(path.dirname(paths.cursor))) {
|
|
143
|
+
const next = withServer(readJson(paths.cursor), cursorEntry(target));
|
|
144
|
+
if (opts.dryRun) log(` would write ${paths.cursor}:\n${indent(JSON.stringify(next.mcpServers.kontex, null, 2))}`);
|
|
145
|
+
else writeJson(paths.cursor, next);
|
|
146
|
+
log(`✓ ${LABELS.cursor}`);
|
|
147
|
+
configured++;
|
|
148
|
+
} else log(`– ${LABELS.cursor} (not installed, skipped)`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return finish(log, configured, notes, 0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (opts.command === "disconnect") {
|
|
155
|
+
log(opts.dryRun ? "Dry run: nothing will be written." : "Removing Kontex from your AI tools…");
|
|
156
|
+
let removed = 0;
|
|
157
|
+
for (const [id, file] of /** @type {const} */ ([["claude-desktop", paths.desktop], ["cursor", paths.cursor]])) {
|
|
158
|
+
if (!wanted.includes(id)) continue;
|
|
159
|
+
const { config, removed: had } = withoutServer(readJson(file));
|
|
160
|
+
if (had) {
|
|
161
|
+
if (!opts.dryRun) writeJson(file, config);
|
|
162
|
+
log(`✓ ${LABELS[id]}`);
|
|
163
|
+
removed++;
|
|
164
|
+
} else log(`– ${LABELS[id]} (nothing to remove)`);
|
|
165
|
+
}
|
|
166
|
+
if (wanted.includes("claude-code")) {
|
|
167
|
+
if (claudeCodeInstalled()) {
|
|
168
|
+
const r = opts.dryRun ? { ok: true } : runClaude(claudeCodeRemoveArgs());
|
|
169
|
+
log(r.ok ? `✓ ${LABELS["claude-code"]}` : `– ${LABELS["claude-code"]} (nothing to remove)`);
|
|
170
|
+
if (r.ok) removed++;
|
|
171
|
+
} else log(`– ${LABELS["claude-code"]} (not installed, skipped)`);
|
|
172
|
+
}
|
|
173
|
+
log(removed ? "Done. Your key still works until you revoke it on the MCP keys page." : "Kontex was not configured anywhere.");
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
log(`Unknown command: ${opts.command}\n\n${HELP}`);
|
|
178
|
+
return 2;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {(s: string) => void} log
|
|
183
|
+
* @param {number} configured
|
|
184
|
+
* @param {string[]} notes
|
|
185
|
+
* @param {number} code
|
|
186
|
+
*/
|
|
187
|
+
function finish(log, configured, notes, code) {
|
|
188
|
+
if (configured === 0) {
|
|
189
|
+
log("Nothing configured: none of Claude Desktop, Claude Code, or Cursor was found on this machine.");
|
|
190
|
+
log("Install one of them, or paste the manual snippet from the MCP keys page into another MCP client.");
|
|
191
|
+
return code || 1;
|
|
192
|
+
}
|
|
193
|
+
log(`Done.${notes.length ? " " + notes.join(" ") : ""}`);
|
|
194
|
+
return code;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** @param {string} s */
|
|
198
|
+
function indent(s) {
|
|
199
|
+
return s
|
|
200
|
+
.split("\n")
|
|
201
|
+
.map((l) => " " + l)
|
|
202
|
+
.join("\n");
|
|
203
|
+
}
|
package/lib/clients.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pure config logic for each MCP client. Nothing here touches the network;
|
|
8
|
+
* file access is limited to the helpers at the bottom so `--dry-run` can
|
|
9
|
+
* reuse the same merge functions.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const SERVER_NAME = "kontex";
|
|
13
|
+
export const DEFAULT_URL = "https://www.kontex.so/api/mcp";
|
|
14
|
+
const ENV_NAME = "KONTEX_AUTH";
|
|
15
|
+
|
|
16
|
+
/** @typedef {{ url: string; key: string }} Target */
|
|
17
|
+
/** @typedef {Record<string, any>} JsonObject */
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Kontex keys are `kx_` + 32 random bytes in base64url.
|
|
21
|
+
* @param {unknown} value
|
|
22
|
+
*/
|
|
23
|
+
export function looksLikeKey(value) {
|
|
24
|
+
return typeof value === "string" && /^kx_[A-Za-z0-9_-]{30,}$/.test(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Claude Desktop reads a JSON file per OS. Its Windows build splits args on
|
|
29
|
+
* spaces, so the header value goes through an env var (mcp-remote README).
|
|
30
|
+
* @param {{ platform?: NodeJS.Platform; home?: string; appData?: string }} [opts]
|
|
31
|
+
*/
|
|
32
|
+
export function claudeDesktopConfigPath(opts = {}) {
|
|
33
|
+
const platform = opts.platform ?? process.platform;
|
|
34
|
+
const home = opts.home ?? os.homedir();
|
|
35
|
+
if (platform === "darwin") return path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
36
|
+
if (platform === "win32") return path.join(opts.appData ?? process.env.APPDATA ?? path.join(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
37
|
+
return path.join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @param {{ home?: string }} [opts] */
|
|
41
|
+
export function cursorConfigPath(opts = {}) {
|
|
42
|
+
return path.join(opts.home ?? os.homedir(), ".cursor", "mcp.json");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @param {Target} t */
|
|
46
|
+
export function claudeDesktopEntry({ url, key }) {
|
|
47
|
+
return {
|
|
48
|
+
command: "npx",
|
|
49
|
+
args: ["-y", "mcp-remote", url, "--header", `Authorization:\${${ENV_NAME}}`],
|
|
50
|
+
env: { [ENV_NAME]: `Bearer ${key}` },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @param {Target} t */
|
|
55
|
+
export function cursorEntry({ url, key }) {
|
|
56
|
+
return { url, headers: { Authorization: `Bearer ${key}` } };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Returns a copy of `config` with our server set, every other server kept.
|
|
61
|
+
* @param {JsonObject | null | undefined} config
|
|
62
|
+
* @param {JsonObject} entry
|
|
63
|
+
*/
|
|
64
|
+
export function withServer(config, entry) {
|
|
65
|
+
const base = config && typeof config === "object" && !Array.isArray(config) ? config : {};
|
|
66
|
+
const servers = base.mcpServers && typeof base.mcpServers === "object" ? base.mcpServers : {};
|
|
67
|
+
return { ...base, mcpServers: { ...servers, [SERVER_NAME]: entry } };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {JsonObject | null | undefined} config
|
|
72
|
+
* @returns {{ config: JsonObject; removed: boolean }}
|
|
73
|
+
*/
|
|
74
|
+
export function withoutServer(config) {
|
|
75
|
+
const base = config && typeof config === "object" && !Array.isArray(config) ? config : {};
|
|
76
|
+
const servers = base.mcpServers && typeof base.mcpServers === "object" ? { ...base.mcpServers } : {};
|
|
77
|
+
const removed = SERVER_NAME in servers;
|
|
78
|
+
delete servers[SERVER_NAME];
|
|
79
|
+
return { config: { ...base, mcpServers: servers }, removed };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Reads a JSON config, tolerating a missing file. A file that exists but
|
|
84
|
+
* does not parse is an error: overwriting it could destroy other servers.
|
|
85
|
+
* @param {string} file
|
|
86
|
+
* @returns {JsonObject | null}
|
|
87
|
+
*/
|
|
88
|
+
export function readJson(file) {
|
|
89
|
+
if (!fs.existsSync(file)) return null;
|
|
90
|
+
const text = fs.readFileSync(file, "utf8");
|
|
91
|
+
if (text.trim() === "") return null;
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(text);
|
|
94
|
+
} catch {
|
|
95
|
+
throw new Error(`${file} is not valid JSON. Fix or remove it, then run this again.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Writes JSON with a `.bak` of the previous contents next to it.
|
|
101
|
+
* @param {string} file
|
|
102
|
+
* @param {JsonObject} data
|
|
103
|
+
*/
|
|
104
|
+
export function writeJson(file, data) {
|
|
105
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
106
|
+
if (fs.existsSync(file)) fs.copyFileSync(file, `${file}.bak`);
|
|
107
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2) + "\n");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** @param {string} file */
|
|
111
|
+
export function dirExists(file) {
|
|
112
|
+
try {
|
|
113
|
+
return fs.statSync(file).isDirectory();
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kontex-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Connect Kontex to Claude Desktop, Claude Code, and Cursor with one command.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kontex-mcp": "bin/kontex-mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"lib",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/gzariaga/kontex.git",
|
|
21
|
+
"directory": "packages/kontex-mcp"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://www.kontex.so",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"kontex",
|
|
26
|
+
"mcp",
|
|
27
|
+
"claude",
|
|
28
|
+
"cursor",
|
|
29
|
+
"customer-intelligence"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"check": "tsc -p tsconfig.json"
|
|
33
|
+
}
|
|
34
|
+
}
|