@arkheia/mcp-server 0.1.4 → 0.1.6

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/package.json CHANGED
@@ -1,56 +1,36 @@
1
- {
2
- "name": "@arkheia/mcp-server",
3
- "version": "0.1.4",
4
- "mcpName": "io.github.arkheiaai/mcp-server",
5
- "description": "Arkheia MCP Server — Fabrication detection for LLM outputs. Detect hallucination in any model's output with a single tool call.",
6
- "bin": {
7
- "mcp-server": "bin/arkheia-mcp.js"
8
- },
9
- "scripts": {
10
- "start": "node bin/arkheia-mcp.js",
11
- "postinstall": "node scripts/setup.js"
12
- },
13
- "keywords": [
14
- "mcp",
15
- "mcp-server",
16
- "model-context-protocol",
17
- "ai-safety",
18
- "ai-governance",
19
- "fabrication-detection",
20
- "hallucination-detection",
21
- "llm-verification",
22
- "llm-grounding",
23
- "ai-agent",
24
- "agent-governance",
25
- "ai-trust",
26
- "ai-audit",
27
- "ai-compliance",
28
- "fact-checking",
29
- "claude",
30
- "gpt",
31
- "gemini",
32
- "llama",
33
- "anthropic",
34
- "openai",
35
- "cursor",
36
- "windsurf",
37
- "cline",
38
- "copilot"
39
- ],
40
- "author": "Arkheia AI <support@arkheia.ai>",
41
- "license": "MIT",
42
- "homepage": "https://arkheia.ai/mcp",
43
- "repository": {
44
- "type": "git",
45
- "url": "git+https://github.com/arkheiaai/arkheia-mcp.git"
46
- },
47
- "engines": {
48
- "node": ">=18.0.0"
49
- },
50
- "files": [
51
- "bin/",
52
- "scripts/",
53
- "python/",
54
- "README.md"
55
- ]
56
- }
1
+ {
2
+ "name": "@arkheia/mcp-server",
3
+ "version": "0.1.6",
4
+ "description": "Arkheia MCP Server — Fabrication detection for AI agents. Know when your AI is making things up.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mcp-server": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/index.js",
12
+ "prepublishOnly": "npm run build",
13
+ "postinstall": "node scripts/setup.js"
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "scripts/",
18
+ "README.md"
19
+ ],
20
+ "keywords": ["mcp", "ai", "fabrication", "detection", "hallucination", "governance"],
21
+ "author": "Arkheia AI <dmurfet@arkheia.ai>",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.12.0",
28
+ "sql.js": "^1.11.0",
29
+ "zod": "^3.23.8"
30
+ },
31
+ "devDependencies": {
32
+ "@types/sql.js": "^1.4.9",
33
+ "@types/node": "^20.14.10",
34
+ "typescript": "^5.5.3"
35
+ }
36
+ }
package/scripts/setup.js CHANGED
@@ -54,16 +54,27 @@ function saveConfig(apiKey) {
54
54
  }
55
55
 
56
56
  function checkPython() {
57
- const candidates = ["python3", "python"];
57
+ // Try versioned interpreters first — on Homebrew, keg-only formulae like
58
+ // python@3.12 only expose python3.12, not python3.
59
+ const candidates = ["python3.13", "python3.12", "python3.11", "python3", "python"];
58
60
  for (const cmd of candidates) {
59
61
  try {
60
- const version = execSync(`${cmd} --version 2>&1`, {
61
- encoding: "utf-8",
62
- timeout: 5000,
63
- }).trim();
64
- const match = version.match(/Python (\d+)\.(\d+)/);
65
- if (match && parseInt(match[1]) >= 3 && parseInt(match[2]) >= 10) {
66
- return { cmd, version };
62
+ // Verify version AND that pyexpat + ensurepip work.
63
+ // Python 3.14 on macOS has broken pyexpat (missing libexpat symbol).
64
+ const output = execSync(
65
+ `${cmd} -c "import sys,pyexpat,ensurepip; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`,
66
+ { encoding: "utf-8", timeout: 10000, stdio: ["pipe", "pipe", "pipe"] }
67
+ ).trim();
68
+ const match = output.match(/^(\d+)\.(\d+)$/);
69
+ if (match) {
70
+ const major = parseInt(match[1]);
71
+ const minor = parseInt(match[2]);
72
+ if (major === 3 && minor >= 10 && minor <= 13) {
73
+ const version = execSync(`${cmd} --version 2>&1`, {
74
+ encoding: "utf-8", timeout: 5000,
75
+ }).trim();
76
+ return { cmd, version };
77
+ }
67
78
  }
68
79
  } catch {
69
80
  // Try next
@@ -75,19 +86,30 @@ function checkPython() {
75
86
  const python = checkPython();
76
87
 
77
88
  if (!python) {
78
- console.log(`
89
+ console.error(`
79
90
  ============================================================
80
- Arkheia MCP Server requires Python 3.10+
91
+ ERROR: Arkheia MCP Server requires Python 3.10–3.13
92
+ with working pyexpat and ensurepip.
93
+
94
+ macOS (Homebrew):
95
+ brew install python@3.12
96
+
97
+ NOTE: Homebrew's current default 'brew install python'
98
+ installs 3.14, which has a broken pyexpat link on macOS
99
+ as of April 2026. Use python@3.12 until Homebrew ships a fix.
100
+
101
+ After installing, verify with:
102
+ python3.12 -c "import pyexpat, ensurepip"
81
103
 
82
- Install Python from: https://python.org
83
- Then run: npx @arkheia/mcp-server
104
+ Other platforms: https://python.org
84
105
  ============================================================
85
106
  `);
107
+ process.exit(1);
86
108
  } else {
87
109
  console.log(`
88
110
  ============================================================
89
111
  Arkheia MCP Server installed successfully.
90
- Python: ${python.version}
112
+ Python: ${python.version} (${python.cmd})
91
113
  ============================================================
92
114
  `);
93
115
  }
@@ -1,207 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Arkheia MCP Server — thin Node wrapper that spawns the Python MCP server.
4
- *
5
- * This wrapper exists so that MCP clients can install via:
6
- * npx @arkheia/mcp-server
7
- * npm install -g @arkheia/mcp-server
8
- *
9
- * It:
10
- * 1. Locates a Python 3.10+ interpreter
11
- * 2. Ensures mcp_server dependencies are installed (pip install)
12
- * 3. Spawns `python -m mcp_server.server` with stdio transport
13
- * 4. Forwards stdin/stdout/stderr (MCP uses stdio)
14
- *
15
- * Environment variables:
16
- * ARKHEIA_API_KEY — API key for hosted detection (required)
17
- * ARKHEIA_PROXY_URL — Local proxy URL (optional, for enterprise)
18
- * ARKHEIA_HOSTED_URL — Hosted API URL (default: https://arkheia-proxy-production.up.railway.app)
19
- */
20
-
21
- const { spawn, execSync } = require("child_process");
22
- const path = require("path");
23
- const fs = require("fs");
24
-
25
- const ARKHEIA_HOME = path.join(
26
- process.env.HOME || process.env.USERPROFILE || "/tmp",
27
- ".arkheia"
28
- );
29
- const REPO_DIR = path.join(ARKHEIA_HOME, "mcp");
30
- const BUNDLED_PYTHON_DIR = path.join(__dirname, "..", "python");
31
- const VENV_DIR = path.join(ARKHEIA_HOME, "venv");
32
-
33
- // Determine the real Python source: cloned repo > bundled package
34
- function getServerDir() {
35
- // If repo already cloned, use it
36
- if (fs.existsSync(path.join(REPO_DIR, "mcp_server", "server.py"))) {
37
- return REPO_DIR;
38
- }
39
- // If bundled package has the server code, use it
40
- if (fs.existsSync(path.join(BUNDLED_PYTHON_DIR, "mcp_server", "server.py"))) {
41
- return BUNDLED_PYTHON_DIR;
42
- }
43
- // Neither exists — clone the repo
44
- process.stderr.write("[arkheia] Server code not found. Cloning from GitHub...\n");
45
- try {
46
- if (!fs.existsSync(ARKHEIA_HOME)) fs.mkdirSync(ARKHEIA_HOME, { recursive: true });
47
- execSync(`git clone --depth 1 https://github.com/arkheiaai/arkheia-mcp.git "${REPO_DIR}"`, {
48
- stdio: "inherit",
49
- timeout: 60000,
50
- });
51
- process.stderr.write("[arkheia] Repository cloned successfully.\n");
52
- return REPO_DIR;
53
- } catch (err) {
54
- process.stderr.write(
55
- `[arkheia] Error: Could not clone repository: ${err.message}\n` +
56
- "Manual install: git clone https://github.com/arkheiaai/arkheia-mcp.git ~/.arkheia/mcp\n"
57
- );
58
- process.exit(1);
59
- }
60
- }
61
-
62
- const PYTHON_DIR = getServerDir();
63
- const REQUIREMENTS = fs.existsSync(path.join(PYTHON_DIR, "mcp_server", "requirements.txt"))
64
- ? path.join(PYTHON_DIR, "mcp_server", "requirements.txt")
65
- : path.join(PYTHON_DIR, "requirements.txt");
66
-
67
- function findPython() {
68
- const candidates = ["python3", "python"];
69
- for (const cmd of candidates) {
70
- try {
71
- const version = execSync(`${cmd} --version 2>&1`, {
72
- encoding: "utf-8",
73
- timeout: 5000,
74
- }).trim();
75
- const match = version.match(/Python (\d+)\.(\d+)/);
76
- if (match && parseInt(match[1]) >= 3 && parseInt(match[2]) >= 10) {
77
- return cmd;
78
- }
79
- } catch {
80
- // Try next candidate
81
- }
82
- }
83
- return null;
84
- }
85
-
86
- function ensureVenv(python) {
87
- const venvPython =
88
- process.platform === "win32"
89
- ? path.join(VENV_DIR, "Scripts", "python.exe")
90
- : path.join(VENV_DIR, "bin", "python");
91
-
92
- if (!fs.existsSync(venvPython)) {
93
- process.stderr.write("[arkheia] Creating virtual environment...\n");
94
- execSync(`${python} -m venv "${VENV_DIR}"`, { stdio: "inherit" });
95
- }
96
-
97
- return venvPython;
98
- }
99
-
100
- function installDeps(venvPython) {
101
- const marker = path.join(VENV_DIR, ".arkheia-deps-installed");
102
- if (fs.existsSync(marker)) {
103
- return; // Already installed
104
- }
105
-
106
- process.stderr.write("[arkheia] Installing dependencies...\n");
107
- execSync(`"${venvPython}" -m pip install --quiet -r "${REQUIREMENTS}"`, {
108
- stdio: "inherit",
109
- timeout: 120000,
110
- });
111
-
112
- fs.writeFileSync(marker, new Date().toISOString());
113
- }
114
-
115
- function main() {
116
- const python = findPython();
117
- if (!python) {
118
- process.stderr.write(
119
- "[arkheia] Error: Python 3.10+ is required but not found.\n" +
120
- "Install Python from https://python.org and try again.\n"
121
- );
122
- process.exit(1);
123
- }
124
-
125
- // ── Load config from ~/.arkheia/config.json ──────────────────
126
- const configPath = path.join(
127
- process.env.HOME || process.env.USERPROFILE || "/tmp",
128
- ".arkheia",
129
- "config.json"
130
- );
131
- let arkheiaConfig = {};
132
- try {
133
- if (fs.existsSync(configPath)) {
134
- arkheiaConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
135
- process.stderr.write(`[arkheia] Loaded config from ${configPath}\n`);
136
- }
137
- } catch (err) {
138
- process.stderr.write(
139
- `[arkheia] Warning: Could not read ${configPath}: ${err.message}\n`
140
- );
141
- }
142
-
143
- // Inject API key from config if not already in env
144
- if (!process.env.ARKHEIA_API_KEY && arkheiaConfig.api_key) {
145
- process.env.ARKHEIA_API_KEY = arkheiaConfig.api_key;
146
- process.stderr.write("[arkheia] API key loaded from config.json\n");
147
- }
148
-
149
- // Inject hosted URL from config if not already in env
150
- if (!process.env.ARKHEIA_HOSTED_URL && arkheiaConfig.proxy_url) {
151
- process.env.ARKHEIA_HOSTED_URL = arkheiaConfig.proxy_url;
152
- process.stderr.write(`[arkheia] Hosted URL: ${arkheiaConfig.proxy_url}\n`);
153
- }
154
-
155
- // Check for API key
156
- if (!process.env.ARKHEIA_API_KEY) {
157
- process.stderr.write(
158
- "[arkheia] Warning: ARKHEIA_API_KEY not set.\n" +
159
- "Get a free API key at https://arkheia.ai/mcp\n" +
160
- "Then set: export ARKHEIA_API_KEY=ak_live_...\n\n"
161
- );
162
- }
163
-
164
- let venvPython;
165
- try {
166
- venvPython = ensureVenv(python);
167
- installDeps(venvPython);
168
- } catch (err) {
169
- process.stderr.write(
170
- `[arkheia] Error setting up Python environment: ${err.message}\n`
171
- );
172
- process.exit(1);
173
- }
174
-
175
- // Spawn the MCP server with stdio transport
176
- const child = spawn(
177
- venvPython,
178
- ["-m", "mcp_server.server"],
179
- {
180
- cwd: PYTHON_DIR,
181
- stdio: ["pipe", "pipe", "inherit"], // stdin/stdout piped, stderr inherited
182
- env: {
183
- ...process.env,
184
- PYTHONPATH: PYTHON_DIR,
185
- },
186
- }
187
- );
188
-
189
- // Forward stdio for MCP protocol
190
- process.stdin.pipe(child.stdin);
191
- child.stdout.pipe(process.stdout);
192
-
193
- child.on("error", (err) => {
194
- process.stderr.write(`[arkheia] Failed to start MCP server: ${err.message}\n`);
195
- process.exit(1);
196
- });
197
-
198
- child.on("exit", (code) => {
199
- process.exit(code || 0);
200
- });
201
-
202
- // Forward signals
203
- process.on("SIGINT", () => child.kill("SIGINT"));
204
- process.on("SIGTERM", () => child.kill("SIGTERM"));
205
- }
206
-
207
- main();
File without changes
@@ -1,4 +0,0 @@
1
- mcp>=1.26.0
2
- httpx>=0.27.1
3
- pydantic>=2.10.0
4
- pyyaml>=6.0