@apogea/apports-mcp 1.0.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 +20 -0
- package/bin/cli.js +105 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @apports/mcp-cli
|
|
2
|
+
|
|
3
|
+
Puente de línea de comandos para conectar clientes MCP locales (Claude Desktop) con el servidor backend de Apports.
|
|
4
|
+
|
|
5
|
+
## Uso con npx
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"apports": {
|
|
11
|
+
"command": "npx",
|
|
12
|
+
"args": ["-y", "@apports/mcp-cli"],
|
|
13
|
+
"env": {
|
|
14
|
+
"APPORTS_URL": "[https://appports.ports.gencat.cat/mcp](https://appports.ports.gencat.cat/mcp)",
|
|
15
|
+
"APPORTS_TOKEN": "tu_token_pat_aqui"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const axios = require("axios");
|
|
4
|
+
const readline = require("readline");
|
|
5
|
+
|
|
6
|
+
const API_URL = process.env.APPORTS_URL || "http://localhost:3000/mcp";
|
|
7
|
+
const AUTH_TOKEN = process.env.APPORTS_TOKEN || "";
|
|
8
|
+
|
|
9
|
+
if (!AUTH_TOKEN) {
|
|
10
|
+
console.error("[Apports MCP] Error: APPORTS_TOKEN no configurado.");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
terminal: false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function emitJsonRpc(rawData) {
|
|
21
|
+
if (!rawData) return;
|
|
22
|
+
|
|
23
|
+
if (typeof rawData === "object") {
|
|
24
|
+
console.log(JSON.stringify(rawData));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof rawData === "string") {
|
|
29
|
+
const lines = rawData.split("\n");
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (trimmed.startsWith("data:")) {
|
|
33
|
+
const jsonContent = trimmed.replace(/^data:\s*/, "").trim();
|
|
34
|
+
if (jsonContent) {
|
|
35
|
+
try {
|
|
36
|
+
console.log(JSON.stringify(JSON.parse(jsonContent)));
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.error("[Apports MCP] Error parseando SSE:", jsonContent);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} else if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
42
|
+
try {
|
|
43
|
+
console.log(JSON.stringify(JSON.parse(trimmed)));
|
|
44
|
+
} catch (e) {
|
|
45
|
+
// Ignorar no-JSON
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
rl.on("line", async (line) => {
|
|
53
|
+
if (!line.trim()) return;
|
|
54
|
+
|
|
55
|
+
let payload;
|
|
56
|
+
try {
|
|
57
|
+
payload = JSON.parse(line);
|
|
58
|
+
} catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const response = await axios.post(API_URL, payload, {
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `Bearer ${AUTH_TOKEN}`,
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
Accept: "application/json, text/event-stream",
|
|
68
|
+
},
|
|
69
|
+
responseType: "text",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
emitJsonRpc(response.data);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const status = error.response?.status;
|
|
75
|
+
const responseData = error.response?.data;
|
|
76
|
+
|
|
77
|
+
console.error(
|
|
78
|
+
`[Apports MCP Error ${status || "HTTP"}]`,
|
|
79
|
+
responseData || error.message,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// 🔑 Si es una notificación (no tiene id), NO enviamos respuesta por STDOUT
|
|
83
|
+
if (payload?.id === undefined || payload?.id === null) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let errorMessage = error.message;
|
|
88
|
+
if (typeof responseData === "object" && responseData?.message) {
|
|
89
|
+
errorMessage = responseData.message;
|
|
90
|
+
} else if (typeof responseData === "string") {
|
|
91
|
+
errorMessage = responseData;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const jsonRpcError = {
|
|
95
|
+
jsonrpc: "2.0",
|
|
96
|
+
id: payload.id,
|
|
97
|
+
error: {
|
|
98
|
+
code: status === 401 ? -32001 : -32603,
|
|
99
|
+
message: `[HTTP ${status || "Error"}] ${errorMessage}`,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
console.log(JSON.stringify(jsonRpcError));
|
|
104
|
+
}
|
|
105
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@apogea/apports-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Puente CLI de STDIO a HTTP Streamable para el servidor MCP de Apports",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"apports-mcp-cli": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"axios": "^1.7.0"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"claude-desktop",
|
|
22
|
+
"apports",
|
|
23
|
+
"copilot"
|
|
24
|
+
],
|
|
25
|
+
"author": "Apogea",
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|