glpi-mcp-server 1.0.0 → 1.0.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/index.js +66 -19
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -2,28 +2,75 @@
|
|
|
2
2
|
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
|
|
5
|
-
// Lê argumentos da linha de comando
|
|
5
|
+
// Lê argumentos da linha de comando
|
|
6
6
|
const args = process.argv.slice(2);
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const config = {
|
|
8
|
+
glpiUrl: process.env.GLPI_URL || '',
|
|
9
|
+
clientId: process.env.GLPI_CLIENT_ID || '',
|
|
10
|
+
clientSecret: process.env.GLPI_CLIENT_SECRET || '',
|
|
11
|
+
username: process.env.GLPI_USERNAME || '',
|
|
12
|
+
password: process.env.GLPI_PASSWORD || ''
|
|
13
|
+
};
|
|
9
14
|
|
|
10
15
|
for (let i = 0; i < args.length; i++) {
|
|
11
|
-
if (args[i] === '--url' && args[i + 1]) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
i++;
|
|
17
|
-
}
|
|
16
|
+
if (args[i] === '--glpi-url' && args[i + 1]) { config.glpiUrl = args[i + 1]; i++; }
|
|
17
|
+
else if (args[i] === '--client-id' && args[i + 1]) { config.clientId = args[i + 1]; i++; }
|
|
18
|
+
else if (args[i] === '--client-secret' && args[i + 1]) { config.clientSecret = args[i + 1]; i++; }
|
|
19
|
+
else if (args[i] === '--username' && args[i + 1]) { config.username = args[i + 1]; i++; }
|
|
20
|
+
else if (args[i] === '--password' && args[i + 1]) { config.password = args[i + 1]; i++; }
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
if (!
|
|
21
|
-
console.error("Uso: npx glpi-mcp-server --url <
|
|
22
|
-
console.error("Ou use variáveis de ambiente GLPI_URL e GLPI_TOKEN.");
|
|
23
|
+
if (!config.glpiUrl || !config.clientId || !config.clientSecret || !config.username || !config.password) {
|
|
24
|
+
console.error("Uso: npx glpi-mcp-server --glpi-url <URL> --client-id <ID> --client-secret <SECRET> --username <USER> --password <PASS>");
|
|
23
25
|
process.exit(1);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
//
|
|
28
|
+
// Remove trailing slash if present
|
|
29
|
+
config.glpiUrl = config.glpiUrl.replace(/\/$/, '');
|
|
30
|
+
|
|
31
|
+
let activeToken = null;
|
|
32
|
+
let tokenExpiresAt = 0;
|
|
33
|
+
|
|
34
|
+
// Função para obter ou renovar o token
|
|
35
|
+
async function getValidToken() {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
// Renova se não tiver token ou se faltar menos de 60 segundos para expirar
|
|
38
|
+
if (activeToken && now < tokenExpiresAt - 60000) {
|
|
39
|
+
return activeToken;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const tokenUrl = `${config.glpiUrl}/api.php/token`;
|
|
43
|
+
const body = new URLSearchParams({
|
|
44
|
+
grant_type: 'password',
|
|
45
|
+
username: config.username,
|
|
46
|
+
password: config.password,
|
|
47
|
+
client_id: config.clientId,
|
|
48
|
+
client_secret: config.clientSecret,
|
|
49
|
+
scope: 'email user api inventory status graphql'
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const response = await fetch(tokenUrl, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
55
|
+
body: body.toString()
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`Falha na autenticação OAuth. Status: ${response.status}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const data = await response.json();
|
|
63
|
+
if (!data.access_token) {
|
|
64
|
+
throw new Error("Token não retornado pela API");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
activeToken = data.access_token;
|
|
68
|
+
// expires_in vem em segundos; converte para ms e marca o tempo de expiração
|
|
69
|
+
tokenExpiresAt = now + ((data.expires_in || 3600) * 1000);
|
|
70
|
+
return activeToken;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Inicia a leitura do stdio
|
|
27
74
|
const rl = readline.createInterface({
|
|
28
75
|
input: process.stdin,
|
|
29
76
|
output: process.stdout,
|
|
@@ -34,7 +81,10 @@ rl.on('line', async (line) => {
|
|
|
34
81
|
if (!line.trim()) return;
|
|
35
82
|
|
|
36
83
|
try {
|
|
37
|
-
const
|
|
84
|
+
const token = await getValidToken();
|
|
85
|
+
const mcpUrl = `${config.glpiUrl}/plugins/mcprotocol/ajax/mcp.php`;
|
|
86
|
+
|
|
87
|
+
const response = await fetch(mcpUrl, {
|
|
38
88
|
method: 'POST',
|
|
39
89
|
headers: {
|
|
40
90
|
'Content-Type': 'application/json',
|
|
@@ -44,19 +94,16 @@ rl.on('line', async (line) => {
|
|
|
44
94
|
});
|
|
45
95
|
|
|
46
96
|
const data = await response.text();
|
|
47
|
-
// Repassa a resposta de volta para a IA via stdout
|
|
48
97
|
process.stdout.write(data + "\n");
|
|
49
98
|
|
|
50
99
|
} catch (error) {
|
|
51
|
-
// Em caso de erro de rede, tenta enviar uma resposta de erro no formato JSON-RPC
|
|
52
100
|
const errorMsg = JSON.stringify({
|
|
53
101
|
jsonrpc: "2.0",
|
|
54
|
-
error: { code: -32000, message: `MCP Proxy
|
|
102
|
+
error: { code: -32000, message: `MCP Proxy Error: ${error.message}` },
|
|
55
103
|
id: null
|
|
56
104
|
});
|
|
57
105
|
process.stdout.write(errorMsg + "\n");
|
|
58
106
|
}
|
|
59
107
|
});
|
|
60
108
|
|
|
61
|
-
// Mantém o processo vivo esperando entradas
|
|
62
109
|
process.stdin.resume();
|