glpi-mcp 0.1.0__tar.gz
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.
- glpi_mcp-0.1.0/.dockerignore +9 -0
- glpi_mcp-0.1.0/.env.example +15 -0
- glpi_mcp-0.1.0/.gitignore +9 -0
- glpi_mcp-0.1.0/.gitlab-ci.yml +144 -0
- glpi_mcp-0.1.0/.python-version +1 -0
- glpi_mcp-0.1.0/AGENTS.md +44 -0
- glpi_mcp-0.1.0/Dockerfile +24 -0
- glpi_mcp-0.1.0/PKG-INFO +46 -0
- glpi_mcp-0.1.0/README.md +36 -0
- glpi_mcp-0.1.0/k8s/deploy.yml +103 -0
- glpi_mcp-0.1.0/opencode.json +22 -0
- glpi_mcp-0.1.0/pyproject.toml +24 -0
- glpi_mcp-0.1.0/src/glpi_mcp/__init__.py +4 -0
- glpi_mcp-0.1.0/src/glpi_mcp/__main__.py +3 -0
- glpi_mcp-0.1.0/src/glpi_mcp/auth.py +55 -0
- glpi_mcp-0.1.0/src/glpi_mcp/client.py +151 -0
- glpi_mcp-0.1.0/src/glpi_mcp/config.py +111 -0
- glpi_mcp-0.1.0/src/glpi_mcp/server.py +83 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/__init__.py +0 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/assets.py +257 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/changes.py +197 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/create_tickets.py +288 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/followups.py +152 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/problems.py +191 -0
- glpi_mcp-0.1.0/src/glpi_mcp/tools/tickets.py +375 -0
- glpi_mcp-0.1.0/uv.lock +788 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# URL base del sitio GLPI (obligatoria), sin apirest.php. Ej: https://glpi.ejemplo.com
|
|
2
|
+
GLPI_BASE_URL=
|
|
3
|
+
|
|
4
|
+
# App Token generado en GLPI (Setup > General > API) (obligatorio)
|
|
5
|
+
GLPI_APP_TOKEN=
|
|
6
|
+
|
|
7
|
+
# User Token del usuario de API (obligatorio)
|
|
8
|
+
GLPI_USER_TOKEN=
|
|
9
|
+
|
|
10
|
+
# Transporte del servidor MCP: stdio | streamable-http (o alias http) | sse (default: stdio)
|
|
11
|
+
TRANSPORT=stdio
|
|
12
|
+
|
|
13
|
+
# Config solo para transporte http/sse
|
|
14
|
+
HOST=127.0.0.1
|
|
15
|
+
PORT=8000
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
image: alpine:3.17.3
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
- name: docker:dind
|
|
5
|
+
|
|
6
|
+
stages:
|
|
7
|
+
- test
|
|
8
|
+
- build
|
|
9
|
+
- environment
|
|
10
|
+
- deploy
|
|
11
|
+
|
|
12
|
+
sonar:
|
|
13
|
+
except:
|
|
14
|
+
- develop
|
|
15
|
+
- main
|
|
16
|
+
- cicd
|
|
17
|
+
- stage
|
|
18
|
+
stage: test
|
|
19
|
+
tags:
|
|
20
|
+
- sonar
|
|
21
|
+
image:
|
|
22
|
+
name: sonarsource/sonar-scanner-cli:11.1
|
|
23
|
+
entrypoint: ['']
|
|
24
|
+
cache:
|
|
25
|
+
key: '${CI_JOB_NAME}'
|
|
26
|
+
paths:
|
|
27
|
+
- .sonar/cache
|
|
28
|
+
script:
|
|
29
|
+
- sleep 10 && sonar-scanner -X -Dsonar.qualitygate.wait=true
|
|
30
|
+
-Dsonar.host.url=$SONAR_HOST_URL
|
|
31
|
+
-Dsonar.login=$SONAR_TOKEN
|
|
32
|
+
|
|
33
|
+
test:
|
|
34
|
+
except:
|
|
35
|
+
- develop
|
|
36
|
+
- main
|
|
37
|
+
- cicd
|
|
38
|
+
- stage
|
|
39
|
+
stage: test
|
|
40
|
+
tags:
|
|
41
|
+
- npm
|
|
42
|
+
image:
|
|
43
|
+
name: node:22.12.0-alpine
|
|
44
|
+
entrypoint: ['']
|
|
45
|
+
script:
|
|
46
|
+
- npm install
|
|
47
|
+
- npm test
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Build the docker image to deploy
|
|
51
|
+
build:
|
|
52
|
+
image:
|
|
53
|
+
name: alpine:latest
|
|
54
|
+
entrypoint: ['']
|
|
55
|
+
only:
|
|
56
|
+
- develop
|
|
57
|
+
- main
|
|
58
|
+
stage: build
|
|
59
|
+
tags:
|
|
60
|
+
- server3
|
|
61
|
+
script:
|
|
62
|
+
- apk add docker
|
|
63
|
+
- export DOCKER_HOST=$DEV_DOCKER_HOST
|
|
64
|
+
- export DOCKER_IMAGE_NAME=$CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHA
|
|
65
|
+
- docker login gitlab.globalsoftm.com:5050 -u $DOCKER_USER -p $DOCKER_PASS
|
|
66
|
+
|
|
67
|
+
## Build & push docker image
|
|
68
|
+
- docker build -t $DOCKER_IMAGE_NAME .
|
|
69
|
+
- docker push $DOCKER_IMAGE_NAME
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
############ DEV ###########
|
|
73
|
+
|
|
74
|
+
environment:
|
|
75
|
+
image:
|
|
76
|
+
name: rarzate/kubectl-with-wget:latest
|
|
77
|
+
entrypoint: ['']
|
|
78
|
+
only:
|
|
79
|
+
- develop
|
|
80
|
+
stage: environment
|
|
81
|
+
tags:
|
|
82
|
+
- server3
|
|
83
|
+
script:
|
|
84
|
+
# kubectl config
|
|
85
|
+
- kubectl config use-context globalsoft/k8s-integration:desarrollo001
|
|
86
|
+
### CHANGE THIS VARIABLES AS YOU NEEDS ###
|
|
87
|
+
- export DOMAIN=gservicio.com # Change for domain to deploy
|
|
88
|
+
|
|
89
|
+
# Define environment variables
|
|
90
|
+
- export PROJECT_NAME=${CI_PROJECT_NAME//_/-}
|
|
91
|
+
- export DOCKER_IMAGE_NAME=$CI_REGISTRY\\/$CI_PROJECT_NAMESPACE\\/$CI_PROJECT_NAME:$CI_COMMIT_SHA
|
|
92
|
+
- export BRANCH_NAME=`echo $CI_COMMIT_BRANCH | tr '[:upper:]' '[:lower:]'`
|
|
93
|
+
- export DNS_SUBDOMAIN=$BRANCH_NAME.$PROJECT_NAME
|
|
94
|
+
- export DNS_DOMAIN=$DNS_SUBDOMAIN.$DOMAIN
|
|
95
|
+
- export NAME_SPACE=$BRANCH_NAME-$PROJECT_NAME
|
|
96
|
+
|
|
97
|
+
# Create namespace
|
|
98
|
+
- kubectl get ns $NAME_SPACE || kubectl create ns $NAME_SPACE
|
|
99
|
+
- kubectl config set-context --current --namespace $NAME_SPACE
|
|
100
|
+
|
|
101
|
+
# Create secret for registry
|
|
102
|
+
- kubectl get secret gitlab-secret || kubectl create secret docker-registry gitlab-secret --docker-server=gitlab.globalsoftm.com:5050 --docker-username=$GITLAB_SECRET_USER --docker-password=$GITLAB_SECRET_PASSWORD --docker-email=$GITLAB_SECRET_EMAIL
|
|
103
|
+
|
|
104
|
+
# Create configmap for env vars
|
|
105
|
+
- env | grep "${CI_COMMIT_BRANCH}_ENV" | sed -e "s/${CI_COMMIT_BRANCH}_ENV=//" | base64 -d > env.txt
|
|
106
|
+
- kubectl get cm envvars && kubectl delete cm envvars || echo "Configmap does not extsts"
|
|
107
|
+
- kubectl create configmap envvars --from-file=env.txt
|
|
108
|
+
|
|
109
|
+
# Show result
|
|
110
|
+
- echo "DNS created $DNS_DOMAIN || K8s namespace created $NAME_SPACE"
|
|
111
|
+
|
|
112
|
+
deploy:
|
|
113
|
+
image:
|
|
114
|
+
name: rarzate/kubectl-with-wget:latest
|
|
115
|
+
entrypoint: ['']
|
|
116
|
+
only:
|
|
117
|
+
- develop
|
|
118
|
+
stage: deploy
|
|
119
|
+
tags:
|
|
120
|
+
- server3
|
|
121
|
+
script:
|
|
122
|
+
# kubectl config
|
|
123
|
+
- kubectl config use-context globalsoft/k8s-integration:desarrollo001
|
|
124
|
+
### CHANGE THIS VARIABLES AS YOUR NEEDS ###
|
|
125
|
+
- export DOMAIN=gservicio.com # Change for domain to deploy
|
|
126
|
+
|
|
127
|
+
# Define environment variables
|
|
128
|
+
- export PROJECT_NAME=${CI_PROJECT_NAME//_/-}
|
|
129
|
+
- export DOCKER_IMAGE_NAME=$CI_REGISTRY\\/$CI_PROJECT_NAMESPACE\\/$CI_PROJECT_NAME:$CI_COMMIT_SHA
|
|
130
|
+
- export BRANCH_NAME=`echo $CI_COMMIT_BRANCH | tr '[:upper:]' '[:lower:]'`
|
|
131
|
+
- export DNS_SUBDOMAIN=$BRANCH_NAME.$PROJECT_NAME
|
|
132
|
+
- export DNS_DOMAIN=$DNS_SUBDOMAIN.$DOMAIN
|
|
133
|
+
- export NAME_SPACE=$BRANCH_NAME-$PROJECT_NAME
|
|
134
|
+
|
|
135
|
+
# Set namespace
|
|
136
|
+
- kubectl config set-context --current --namespace $NAME_SPACE
|
|
137
|
+
|
|
138
|
+
# Deploy application
|
|
139
|
+
- sed -i "s/\$DOCKER_IMAGE_NAME/$DOCKER_IMAGE_NAME/g" k8s/deploy.yml
|
|
140
|
+
- sed -i "s/\$DNS_DOMAIN/$DNS_DOMAIN/g" k8s/deploy.yml
|
|
141
|
+
- kubectl delete deployment backend && sleep 10
|
|
142
|
+
- kubectl apply -f k8s/deploy.yml
|
|
143
|
+
- echo "Application deployed on https://$DNS_DOMAIN K8s namespace created $NAME_SPACE"
|
|
144
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
glpi_mcp-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Qué es
|
|
4
|
+
|
|
5
|
+
Servidor MCP en Python que expone tools para consultar la API REST de GLPI
|
|
6
|
+
vía HTTP. Stack: SDK `mcp` v1 (`MCPServer`, no FastMCP) + `httpx`, gestionado
|
|
7
|
+
con `uv` (Python 3.12, `requires-python >=3.11`).
|
|
8
|
+
|
|
9
|
+
## Comandos
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
uv run glpi-mcp # arranca el servidor (entrypoint server:main)
|
|
13
|
+
uv run python -m glpi_mcp # equivalente
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Transporte controlado por `TRANSPORT`: `stdio` (default) | `streamable-http` (alias `http`) | `sse`. Para http/sse se usan `HOST`/`PORT` (default 127.0.0.1:8000).
|
|
17
|
+
- No hay tests, linter, formatter ni CI configurados. Verificación mínima:
|
|
18
|
+
`uv run python -c "import glpi_mcp.server; from glpi_mcp.config import get_settings; get_settings()"` (falla con `SettingsError` si no hay `.env`).
|
|
19
|
+
|
|
20
|
+
## Configuración
|
|
21
|
+
|
|
22
|
+
- `.env` es obligatorio y está gitignored; copiar `.env.example`. Requiere 3 variables:
|
|
23
|
+
`GLPI_BASE_URL` (raíz del GLPI, sin `/apirest.php`), `GLPI_APP_TOKEN` (Setup > General > API) y
|
|
24
|
+
`GLPI_USER_TOKEN` (del usuario de API). Sin ellas el server sale con error.
|
|
25
|
+
`.env` se carga desde el cwd o desde la raíz del repo (`config._load_env`).
|
|
26
|
+
- **Autenticación**: la API de GLPI usa App Token + token de sesión. `client.request`
|
|
27
|
+
inicia una sesión por cada petición (initSession) y la cierra (killSession).
|
|
28
|
+
Los paths de endpoints deben usar el prefijo `/apirest.php/`.
|
|
29
|
+
|
|
30
|
+
## Agregar una tool (flujo principal de trabajo)
|
|
31
|
+
|
|
32
|
+
1. Crea `src/glpi_mcp/tools/<modulo>.py` con una función `register(mcp: MCPServer) -> None` y tools decoradas con `@mcp.tool()`.
|
|
33
|
+
2. Regístralo en `TOOL_MODULES` de `src/glpi_mcp/server.py`.
|
|
34
|
+
|
|
35
|
+
Detalles no obvios:
|
|
36
|
+
- Las tools devuelven `str` (JSON serializado), no objetos; los `ApiError` se capturan y se devuelven como string.
|
|
37
|
+
- `client.request(method, path, params, json_body)` devuelve JSON parseado o texto; los paths se normalizan con `/` inicial.
|
|
38
|
+
- `client.init_session` / `client.kill_session` son públicos por si alguna tool necesita control manual de la sesión.
|
|
39
|
+
|
|
40
|
+
## Gotchas
|
|
41
|
+
|
|
42
|
+
- La fuente de verdad de las tools registradas es `server.py:TOOL_MODULES` (actualmente vacío: aún no hay tools).
|
|
43
|
+
- `base_url` NO debe incluir `/apirest.php`; los paths de los endpoints sí lo llevan.
|
|
44
|
+
- Rama de git por defecto: `develop`. Sin commits iniciales todavía.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
|
|
4
|
+
|
|
5
|
+
ENV UV_COMPILE_BYTECODE=1 \
|
|
6
|
+
UV_LINK_MODE=copy \
|
|
7
|
+
PATH="/app/.venv/bin:$PATH"
|
|
8
|
+
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
COPY pyproject.toml uv.lock ./
|
|
12
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
13
|
+
uv sync --frozen --no-dev --no-install-project
|
|
14
|
+
|
|
15
|
+
COPY README.md ./
|
|
16
|
+
COPY src ./src
|
|
17
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
18
|
+
uv sync --frozen --no-dev
|
|
19
|
+
|
|
20
|
+
RUN useradd --create-home --uid 1000 mcpuser && chown -R mcpuser:mcpuser /app
|
|
21
|
+
USER mcpuser
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
CMD ["glpi-mcp"]
|
glpi_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: glpi-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server con tools para consultar la API REST de GLPI
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: httpx>=0.27.0
|
|
7
|
+
Requires-Dist: mcp>=1.0.0
|
|
8
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# GLPI MCP
|
|
12
|
+
|
|
13
|
+
Servidor MCP en Python para interactuar con la [API REST de GLPI](https://glpi-project.org) (App Token + User Token).
|
|
14
|
+
|
|
15
|
+
## Requisitos
|
|
16
|
+
|
|
17
|
+
- Python 3.11+ (desarrollado con 3.12)
|
|
18
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
19
|
+
|
|
20
|
+
## Configuración
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
cp .env.example .env
|
|
24
|
+
# Rellena GLPI_BASE_URL, GLPI_APP_TOKEN y GLPI_USER_TOKEN
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Uso
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
uv sync
|
|
31
|
+
uv run glpi-mcp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Transportes
|
|
35
|
+
|
|
36
|
+
`TRANSPORT` controla el transporte MCP: `stdio` (default), `streamable-http` (o `http`) y `sse`.
|
|
37
|
+
|
|
38
|
+
## Estructura
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
src/glpi_mcp/
|
|
42
|
+
├── client.py # Cliente HTTP con auth por sesión GLPI
|
|
43
|
+
├── config.py # Settings desde variables de entorno
|
|
44
|
+
├── server.py # Registro de tools y arranque
|
|
45
|
+
└── tools/ # Módulos de tools (uno por área)
|
|
46
|
+
```
|
glpi_mcp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# GLPI MCP
|
|
2
|
+
|
|
3
|
+
Servidor MCP en Python para interactuar con la [API REST de GLPI](https://glpi-project.org) (App Token + User Token).
|
|
4
|
+
|
|
5
|
+
## Requisitos
|
|
6
|
+
|
|
7
|
+
- Python 3.11+ (desarrollado con 3.12)
|
|
8
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
9
|
+
|
|
10
|
+
## Configuración
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
cp .env.example .env
|
|
14
|
+
# Rellena GLPI_BASE_URL, GLPI_APP_TOKEN y GLPI_USER_TOKEN
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Uso
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
uv sync
|
|
21
|
+
uv run glpi-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Transportes
|
|
25
|
+
|
|
26
|
+
`TRANSPORT` controla el transporte MCP: `stdio` (default), `streamable-http` (o `http`) y `sse`.
|
|
27
|
+
|
|
28
|
+
## Estructura
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/glpi_mcp/
|
|
32
|
+
├── client.py # Cliente HTTP con auth por sesión GLPI
|
|
33
|
+
├── config.py # Settings desde variables de entorno
|
|
34
|
+
├── server.py # Registro de tools y arranque
|
|
35
|
+
└── tools/ # Módulos de tools (uno por área)
|
|
36
|
+
```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# apiVersion: v1
|
|
2
|
+
# kind: PersistentVolumeClaim
|
|
3
|
+
# metadata:
|
|
4
|
+
# name: parkit-pvc
|
|
5
|
+
# spec:
|
|
6
|
+
# storageClassName: oci-bv
|
|
7
|
+
# accessModes:
|
|
8
|
+
# - ReadWriteOnce
|
|
9
|
+
# resources:
|
|
10
|
+
# requests:
|
|
11
|
+
# storage: 5Gi
|
|
12
|
+
# ---
|
|
13
|
+
apiVersion: apps/v1
|
|
14
|
+
kind: Deployment
|
|
15
|
+
metadata:
|
|
16
|
+
name: backend
|
|
17
|
+
labels:
|
|
18
|
+
app: backend
|
|
19
|
+
spec:
|
|
20
|
+
replicas: 1
|
|
21
|
+
selector:
|
|
22
|
+
matchLabels:
|
|
23
|
+
app: backend
|
|
24
|
+
strategy:
|
|
25
|
+
rollingUpdate:
|
|
26
|
+
maxUnavailable: 25%
|
|
27
|
+
template:
|
|
28
|
+
metadata:
|
|
29
|
+
labels:
|
|
30
|
+
app: backend
|
|
31
|
+
spec:
|
|
32
|
+
imagePullSecrets:
|
|
33
|
+
- name: gitlab-secret
|
|
34
|
+
containers:
|
|
35
|
+
- image: $DOCKER_IMAGE_NAME
|
|
36
|
+
volumeMounts:
|
|
37
|
+
- name: envvars
|
|
38
|
+
mountPath: '/app/.env'
|
|
39
|
+
subPath: .env
|
|
40
|
+
readOnly: true
|
|
41
|
+
|
|
42
|
+
resources:
|
|
43
|
+
limits:
|
|
44
|
+
cpu: 200m
|
|
45
|
+
memory: 512Mi
|
|
46
|
+
requests:
|
|
47
|
+
cpu: 100m
|
|
48
|
+
memory: 256Mi
|
|
49
|
+
|
|
50
|
+
imagePullPolicy: Always
|
|
51
|
+
name: backend
|
|
52
|
+
ports:
|
|
53
|
+
- containerPort: 8000
|
|
54
|
+
protocol: TCP
|
|
55
|
+
securityContext:
|
|
56
|
+
capabilities:
|
|
57
|
+
drop:
|
|
58
|
+
- all
|
|
59
|
+
add: ['NET_BIND_SERVICE']
|
|
60
|
+
allowPrivilegeEscalation: false
|
|
61
|
+
|
|
62
|
+
restartPolicy: Always
|
|
63
|
+
volumes:
|
|
64
|
+
- name: envvars
|
|
65
|
+
configMap:
|
|
66
|
+
name: envvars
|
|
67
|
+
items:
|
|
68
|
+
- key: env.txt
|
|
69
|
+
path: .env
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
apiVersion: v1
|
|
73
|
+
kind: Service
|
|
74
|
+
metadata:
|
|
75
|
+
name: backend-service
|
|
76
|
+
spec:
|
|
77
|
+
selector:
|
|
78
|
+
app: backend
|
|
79
|
+
ports:
|
|
80
|
+
- port: 80
|
|
81
|
+
protocol: TCP
|
|
82
|
+
targetPort: 8000
|
|
83
|
+
---
|
|
84
|
+
apiVersion: networking.k8s.io/v1
|
|
85
|
+
kind: Ingress
|
|
86
|
+
metadata:
|
|
87
|
+
name: ingress-backed
|
|
88
|
+
annotations:
|
|
89
|
+
kubernetes.io/ingress.class: nginx
|
|
90
|
+
nginx.ingress.kubernetes.io/proxy-body-size: 2048m
|
|
91
|
+
nginx.org/client-max-body-size: 2048m
|
|
92
|
+
spec:
|
|
93
|
+
rules:
|
|
94
|
+
- host: '$DNS_DOMAIN'
|
|
95
|
+
http:
|
|
96
|
+
paths:
|
|
97
|
+
- pathType: Prefix
|
|
98
|
+
path: '/'
|
|
99
|
+
backend:
|
|
100
|
+
service:
|
|
101
|
+
name: backend-service
|
|
102
|
+
port:
|
|
103
|
+
number: 80
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"mcp": {
|
|
4
|
+
// "glpi": {
|
|
5
|
+
// "type": "remote",
|
|
6
|
+
// "url": "https://develop.gs-glpi-mcp.gservicio.com/mcp",
|
|
7
|
+
// "headers": {
|
|
8
|
+
// "Authorization": "Bearer {env:GLPI_MCP_TOKEN}"
|
|
9
|
+
// },
|
|
10
|
+
// "enabled": true
|
|
11
|
+
// }
|
|
12
|
+
"glpi": {
|
|
13
|
+
"type": "local",
|
|
14
|
+
"command": [
|
|
15
|
+
"uv",
|
|
16
|
+
"run",
|
|
17
|
+
"glpi-mcp"
|
|
18
|
+
],
|
|
19
|
+
"enabled": true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "glpi-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP server con tools para consultar la API REST de GLPI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"mcp>=1.0.0",
|
|
9
|
+
"httpx>=0.27.0",
|
|
10
|
+
"python-dotenv>=1.0.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
glpi-mcp = "glpi_mcp.server:main"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/glpi_mcp"]
|
|
22
|
+
|
|
23
|
+
[tool.uv]
|
|
24
|
+
package = true
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Autenticación Bearer para los transportes http/sse.
|
|
2
|
+
|
|
3
|
+
Integra el mecanismo de auth nativo del SDK mcp: el servidor envuelve el
|
|
4
|
+
endpoint con RequireAuthMiddleware cuando se le pasa un TokenVerifier, de
|
|
5
|
+
modo que cada petición sin header `Authorization: Bearer` (o con token
|
|
6
|
+
inválido) responde 401 + WWW-Authenticate.
|
|
7
|
+
|
|
8
|
+
Los tokens se comparan en tiempo constante (secrets.compare_digest) y se
|
|
9
|
+
aceptan varios a la vez para poder rotarlos con solapamiento.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import secrets
|
|
15
|
+
|
|
16
|
+
from mcp.server.auth.provider import AccessToken, TokenVerifier
|
|
17
|
+
from mcp.server.auth.settings import AuthSettings
|
|
18
|
+
|
|
19
|
+
from glpi_mcp.config import Settings
|
|
20
|
+
|
|
21
|
+
# Ruta del endpoint streamable-http que se expone con auth.
|
|
22
|
+
MCP_HTTP_PATH = "/mcp"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StaticTokenVerifier(TokenVerifier):
|
|
26
|
+
"""Valida un bearer token contra una lista fija de tokens permitidos."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, tokens: tuple[str, ...]) -> None:
|
|
29
|
+
self._tokens = tokens
|
|
30
|
+
|
|
31
|
+
async def verify_token(self, token: str) -> AccessToken | None:
|
|
32
|
+
if not token:
|
|
33
|
+
return None
|
|
34
|
+
for valid in self._tokens:
|
|
35
|
+
if valid and secrets.compare_digest(token, valid):
|
|
36
|
+
return AccessToken(token=token, client_id="glpi-mcp", scopes=[])
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def build_auth(settings: Settings) -> tuple[AuthSettings, TokenVerifier] | None:
|
|
41
|
+
"""Construye la configuración de auth del MCPServer.
|
|
42
|
+
|
|
43
|
+
Devuelve None si no hay tokens configurados (p. ej. transporte stdio).
|
|
44
|
+
issuer_url y resource_server_url son campos obligatorios de AuthSettings;
|
|
45
|
+
aquí alimentan el metadata RFC 9728 y el header WWW-Authenticate.
|
|
46
|
+
"""
|
|
47
|
+
if not settings.bearer_tokens:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
base_url = f"http://{settings.host}:{settings.port}"
|
|
51
|
+
auth_settings = AuthSettings(
|
|
52
|
+
issuer_url=base_url,
|
|
53
|
+
resource_server_url=f"{base_url}{MCP_HTTP_PATH}",
|
|
54
|
+
)
|
|
55
|
+
return auth_settings, StaticTokenVerifier(settings.bearer_tokens)
|