@qune-tech/ocds-mcp 0.3.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 ADDED
@@ -0,0 +1,104 @@
1
+ <!-- mcp-name: io.github.qune-tech/ocds-mcp -->
2
+
3
+ # @qune-tech/ocds-mcp
4
+
5
+ MCP server for German public procurement data (OCDS). Connects your AI assistant to the [Vergabe Dashboard](https://vergabe-dashboard.qune.de) API for semantic search, tender matching, and company profile management.
6
+
7
+ Company profiles never leave your machine. GDPR-compliant by design.
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ npx @qune-tech/ocds-mcp --api-key sk_live_YOUR_KEY_HERE
13
+ ```
14
+
15
+ ### Get an API key
16
+
17
+ Sign up at [vergabe-dashboard.qune.de](https://vergabe-dashboard.qune.de) and create an API key (MCP or Enterprise plan required).
18
+
19
+ ## Configure your AI client
20
+
21
+ ### Claude Desktop
22
+
23
+ Edit `claude_desktop_config.json`:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "ocds": {
29
+ "command": "npx",
30
+ "args": ["-y", "@qune-tech/ocds-mcp", "--api-key", "sk_live_YOUR_KEY_HERE"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Claude Code
37
+
38
+ Add `.mcp.json` to your project root:
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "ocds": {
44
+ "command": "npx",
45
+ "args": ["-y", "@qune-tech/ocds-mcp", "--api-key", "sk_live_YOUR_KEY_HERE"]
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Cursor
52
+
53
+ Settings > MCP Servers > Add:
54
+
55
+ - Command: `npx`
56
+ - Args: `-y @qune-tech/ocds-mcp --api-key sk_live_YOUR_KEY_HERE`
57
+
58
+ ## Available Tools
59
+
60
+ | Tool | Description |
61
+ |------|-------------|
62
+ | `search_text` | Semantic search across all tenders |
63
+ | `list_releases` | Filter and browse tenders by month, CPV code, category, value range |
64
+ | `get_release` | Full tender details by OCID |
65
+ | `get_index_info` | Database statistics and connectivity check |
66
+ | `create_company_profile` | Create a matching profile for your company |
67
+ | `update_company_profile` | Update an existing profile |
68
+ | `get_company_profile` | View profile details |
69
+ | `list_company_profiles` | List all your profiles |
70
+ | `delete_company_profile` | Delete a profile |
71
+ | `match_tenders` | Match a profile against all tenders with semantic similarity |
72
+
73
+ ## How It Works
74
+
75
+ The npm package downloads the correct platform-native binary on install. No Node.js runtime dependency for the actual MCP server.
76
+
77
+ ```
78
+ LLM <--stdio--> ocds-mcp (local binary)
79
+ | Local: company profiles + sentence embedder
80
+ | Remote: searches, release queries
81
+ +--HTTPS--> Vergabe Dashboard API
82
+ ```
83
+
84
+ - Company profiles are stored locally (never leave your machine).
85
+ - Text embeddings are computed locally (multilingual-e5-small ONNX model, ~118 MB, auto-downloaded on first run).
86
+ - Only embedding vectors are sent to the API for search and matching.
87
+
88
+ ## Supported Platforms
89
+
90
+ | Platform | Architecture |
91
+ |----------|-------------|
92
+ | Linux | x86_64 |
93
+ | macOS | Apple Silicon (ARM64) |
94
+ | Windows | x86_64 |
95
+
96
+ ## Requirements
97
+
98
+ - An API key from [vergabe-dashboard.qune.de](https://vergabe-dashboard.qune.de)
99
+ - ~200 MB disk space for the ONNX model (auto-downloaded on first run)
100
+ - Internet connection to reach the API
101
+
102
+ ## License
103
+
104
+ MIT
package/index.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawn } = require("child_process");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+
8
+ const binary = process.platform === "win32" ? "ocds-mcp.exe" : "ocds-mcp";
9
+ const binaryPath = path.join(__dirname, "bin", binary);
10
+
11
+ if (!fs.existsSync(binaryPath)) {
12
+ console.error(
13
+ `ocds-mcp binary not found at ${binaryPath}\n` +
14
+ `Run "npm rebuild @qune-tech/ocds-mcp" to trigger the download.`
15
+ );
16
+ process.exit(1);
17
+ }
18
+
19
+ const child = spawn(binaryPath, process.argv.slice(2), {
20
+ stdio: "inherit",
21
+ });
22
+
23
+ child.on("error", (err) => {
24
+ console.error(`Failed to start ocds-mcp: ${err.message}`);
25
+ process.exit(1);
26
+ });
27
+
28
+ child.on("exit", (code) => {
29
+ process.exit(code ?? 1);
30
+ });
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@qune-tech/ocds-mcp",
3
+ "version": "0.3.0",
4
+ "description": "MCP server for German public procurement data (OCDS). Semantic search, tender matching, and company profile management.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/qune-tech/ocds-mcp"
9
+ },
10
+ "homepage": "https://vergabe-dashboard.qune.de",
11
+ "keywords": [
12
+ "mcp",
13
+ "ocds",
14
+ "procurement",
15
+ "tenders",
16
+ "vergabe"
17
+ ],
18
+ "mcpName": "io.github.qune-tech/ocds-mcp",
19
+ "bin": {
20
+ "ocds-mcp": "index.js"
21
+ },
22
+ "scripts": {
23
+ "postinstall": "node postinstall.js"
24
+ },
25
+ "files": [
26
+ "index.js",
27
+ "postinstall.js",
28
+ "README.md"
29
+ ],
30
+ "engines": {
31
+ "node": ">=16"
32
+ }
33
+ }
package/postinstall.js ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const https = require("https");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const { execSync } = require("child_process");
8
+ const os = require("os");
9
+
10
+ const VERSION = require("./package.json").version;
11
+ const REPO = "qune-tech/ocds-mcp";
12
+ const BASE_URL = `https://github.com/${REPO}/releases/download/v${VERSION}`;
13
+
14
+ const PLATFORMS = {
15
+ "linux-x64": {
16
+ asset: "ocds-mcp-linux-x86_64.tar.gz",
17
+ extracted: "ocds-mcp-linux-x86_64",
18
+ binary: "ocds-mcp",
19
+ },
20
+ "darwin-arm64": {
21
+ asset: "ocds-mcp-macos-arm64.tar.gz",
22
+ extracted: "ocds-mcp-macos-arm64",
23
+ binary: "ocds-mcp",
24
+ },
25
+ "win32-x64": {
26
+ asset: "ocds-mcp-windows-x86_64.zip",
27
+ extracted: "ocds-mcp-windows-x86_64.exe",
28
+ binary: "ocds-mcp.exe",
29
+ },
30
+ };
31
+
32
+ function getPlatformKey() {
33
+ return `${process.platform}-${process.arch}`;
34
+ }
35
+
36
+ function download(url) {
37
+ return new Promise((resolve, reject) => {
38
+ const request = (url) => {
39
+ https
40
+ .get(url, (res) => {
41
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
42
+ request(res.headers.location);
43
+ return;
44
+ }
45
+ if (res.statusCode !== 200) {
46
+ reject(new Error(`Download failed: HTTP ${res.statusCode} for ${url}`));
47
+ return;
48
+ }
49
+ const chunks = [];
50
+ res.on("data", (chunk) => chunks.push(chunk));
51
+ res.on("end", () => resolve(Buffer.concat(chunks)));
52
+ res.on("error", reject);
53
+ })
54
+ .on("error", reject);
55
+ };
56
+ request(url);
57
+ });
58
+ }
59
+
60
+ function extractTarGz(buffer, destDir) {
61
+ const archivePath = path.join(destDir, "archive.tar.gz");
62
+ fs.writeFileSync(archivePath, buffer);
63
+ execSync(`tar xzf "${archivePath}" -C "${destDir}"`, { stdio: "ignore" });
64
+ fs.unlinkSync(archivePath);
65
+ }
66
+
67
+ function extractZip(buffer, destDir) {
68
+ const archivePath = path.join(destDir, "archive.zip");
69
+ fs.writeFileSync(archivePath, buffer);
70
+ // Use PowerShell on Windows, unzip on Unix
71
+ if (process.platform === "win32") {
72
+ execSync(
73
+ `powershell -Command "Expand-Archive -Force '${archivePath}' '${destDir}'"`,
74
+ { stdio: "ignore" }
75
+ );
76
+ } else {
77
+ execSync(`unzip -o "${archivePath}" -d "${destDir}"`, { stdio: "ignore" });
78
+ }
79
+ fs.unlinkSync(archivePath);
80
+ }
81
+
82
+ async function main() {
83
+ const key = getPlatformKey();
84
+ const info = PLATFORMS[key];
85
+
86
+ if (!info) {
87
+ console.error(
88
+ `Unsupported platform: ${key}\n` +
89
+ `Supported: ${Object.keys(PLATFORMS).join(", ")}\n` +
90
+ `You can build from source: https://github.com/${REPO}#building-from-source`
91
+ );
92
+ process.exit(1);
93
+ }
94
+
95
+ const binDir = path.join(__dirname, "bin");
96
+ const binaryPath = path.join(binDir, info.binary);
97
+
98
+ // Skip download if binary already exists (cached)
99
+ if (fs.existsSync(binaryPath)) {
100
+ console.log(`ocds-mcp binary already exists at ${binaryPath}`);
101
+ return;
102
+ }
103
+
104
+ const url = `${BASE_URL}/${info.asset}`;
105
+ console.log(`Downloading ocds-mcp v${VERSION} for ${key}...`);
106
+ console.log(` ${url}`);
107
+
108
+ let buffer;
109
+ try {
110
+ buffer = await download(url);
111
+ } catch (err) {
112
+ console.error(`Failed to download ocds-mcp binary: ${err.message}`);
113
+ console.error(
114
+ `\nYou can download it manually from:\n https://github.com/${REPO}/releases/tag/v${VERSION}`
115
+ );
116
+ process.exit(1);
117
+ }
118
+
119
+ fs.mkdirSync(binDir, { recursive: true });
120
+
121
+ if (info.asset.endsWith(".tar.gz")) {
122
+ extractTarGz(buffer, binDir);
123
+ } else if (info.asset.endsWith(".zip")) {
124
+ extractZip(buffer, binDir);
125
+ }
126
+
127
+ // Rename extracted binary to canonical name
128
+ const extractedPath = path.join(binDir, info.extracted);
129
+ if (fs.existsSync(extractedPath) && info.extracted !== info.binary) {
130
+ fs.renameSync(extractedPath, binaryPath);
131
+ }
132
+
133
+ // Make binary executable on Unix
134
+ if (process.platform !== "win32") {
135
+ fs.chmodSync(binaryPath, 0o755);
136
+ }
137
+
138
+ if (!fs.existsSync(binaryPath)) {
139
+ console.error(
140
+ `Binary not found after extraction. Expected: ${binaryPath}\n` +
141
+ `Please report this issue at https://github.com/${REPO}/issues`
142
+ );
143
+ process.exit(1);
144
+ }
145
+
146
+ console.log(`ocds-mcp v${VERSION} installed to ${binaryPath}`);
147
+ }
148
+
149
+ main();