mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
refactor(mcp): make MCP a role topic instead of a service entry
The block sat under services but never behaved like a service. A service has exactly one providing role, declared with provides, so the registry can map the key to that role and consumers bind their flag to its presence. No role declares provides: mcp. There are seventeen providers and the pairing is done by allowed_consumers inside the block itself. Every rule that enforces the service model therefore needed an exemption for it: a shared-provider suppression in fourteen roles, a playwright-service-flag suppression, and two freshly added exemptions in the dynamic-flag and variants-static-override checks. Four independent rules, four escape hatches, one cause. They are all gone now, together with a bond key that only the resource budgeter reads and only for entries under services, and a wordpress addon bridge that pointed at a service block that no longer exists. The block moves to meta/mcp.yml. The applications loader maps meta/<name>.yml to applications.<app>.<name> generically, so the move needs no wiring; the file takes the same shape as meta/rbac.yml. Every config path follows, the variant overrides are lifted out of their services map, and the vocabulary module leaves the services package because it no longer describes one. roles_with_service gains an explicit topic parameter rather than an implicit fallback, so nothing silently resolves against the wrong container. Files that are otherwise unrelated to this move are included where the committed tree would otherwise keep pointing at a path that no longer resolves: thirteen vars files, eleven playwright env templates and one task file.
This commit is contained in:
1 parent
f4398e1c58
commit
06bc856c32
143 files changed
+1632
-660
No files matched your search
@@ -124,6 +124,7 @@ Every invokable role in the tested lifecycle envelope, with its upstream homepag
|
||||
| [Matomo](roles/web-app-matomo/) | beta | Self-hosted, privacy-first web analytics platform for real-time visitor tracking and site metrics | [🌐](https://matomo.org/) [🎬](https://www.youtube.com/watch?v=Qc2kooLNDiU) [🛠️](roles/web-app-matomo/README.md#quick-setup) | [Backup Docker Volumes](roles/svc-bkp-volume-2-local/), [Corporate Design](roles/web-svc-css/), [Dashboard](roles/web-app-dashboard/), [MariaDB](roles/svc-db-mariadb/), [Prometheus](roles/web-app-prometheus/), [Universal Logout](roles/web-svc-logout/) |
|
||||
| [Matrix](roles/web-app-matrix/) | beta | Federated, end-to-end-encrypted messaging platform with a web client and protocol bridges to external chat networks | [🌐](https://matrix.org/) [🎬](https://www.youtube.com/watch?v=3AVsNqH_-9M) [🛠️](roles/web-app-matrix/README.md#quick-setup) | [Backup Docker Volumes](roles/svc-bkp-volume-2-local/), [Corporate Design](roles/web-svc-css/), [Coturn](roles/web-svc-coturn/), [Dashboard](roles/web-app-dashboard/), [Keycloak](roles/web-app-keycloak/), [Mailu](roles/web-app-mailu/), [Matomo](roles/web-app-matomo/), [OpenLDAP](roles/svc-db-openldap/), [PostgreSQL](roles/svc-db-postgres/), [Prometheus](roles/web-app-prometheus/), [SeaweedFS](roles/web-app-seaweedfs/), [Universal Logout](roles/web-svc-logout/) |
|
||||
| [Mattermost](roles/web-app-mattermost/) | beta | Open-source, self-hosted team messaging platform for real-time collaboration via channels, threads, and integrations | [🌐](https://mattermost.com/) [🎬](https://www.youtube.com/watch?v=szfZfQFUSnU) [🛠️](roles/web-app-mattermost/README.md#quick-setup) | [Backup Docker Volumes](roles/svc-bkp-volume-2-local/), [Content Delivery Network](roles/web-svc-cdn/), [Corporate Design](roles/web-svc-css/), [Dashboard](roles/web-app-dashboard/), [Keycloak](roles/web-app-keycloak/), [Mailu](roles/web-app-mailu/), [Matomo](roles/web-app-matomo/), [PostgreSQL](roles/svc-db-postgres/), [Prometheus](roles/web-app-prometheus/), [SeaweedFS](roles/web-app-seaweedfs/), [Universal Logout](roles/web-svc-logout/) |
|
||||
| [MCP Adapter](roles/svc-ai-mcp-adapter/) | alpha | Reusable per-provider MCP adapter exposing a narrow allowlisted read surface | [🌐](https://modelcontextprotocol.io/) [🛠️](roles/svc-ai-mcp-adapter/README.md#quick-setup) | |
|
||||
| [MediaWiki](roles/web-app-mediawiki/) | beta | Collaborative wiki platform for user-driven documentation with an extensible extension ecosystem and content management | [🌐](https://www.mediawiki.org/) [🎬](https://www.youtube.com/watch?v=-JnIjpRvgNY) [🛠️](roles/web-app-mediawiki/README.md#quick-setup) | [Backup Docker Volumes](roles/svc-bkp-volume-2-local/), [Corporate Design](roles/web-svc-css/), [Dashboard](roles/web-app-dashboard/), [Keycloak](roles/web-app-keycloak/), [Mailu](roles/web-app-mailu/), [MariaDB](roles/svc-db-mariadb/), [Matomo](roles/web-app-matomo/), [Prometheus](roles/web-app-prometheus/), [Universal Logout](roles/web-svc-logout/) |
|
||||
| [Memcached](roles/svc-db-memcached/) | beta | Central in-memory caching engine with per-consumer key-prefix isolation, usable centrally or as an embedded sidecar | [🌐](https://memcached.org/) [🛠️](roles/svc-db-memcached/README.md#quick-setup) | |
|
||||
| [MIG](roles/web-app-mig/) | alpha | Interactive graph visualization of all Infinito.Nexus roles and their dependencies for architecture transparency | [🛠️](roles/web-app-mig/README.md#quick-setup) | [Corporate Design](roles/web-svc-css/), [Dashboard](roles/web-app-dashboard/), [Matomo](roles/web-app-matomo/), [Prometheus](roles/web-app-prometheus/) |
|
||||
|
||||
@@ -1,75 +1,133 @@
|
||||
# MCP Service Block 🔌
|
||||
|
||||
A role with a Model Context Protocol surface declares one `mcp:` entry in `meta/services.yml`. The block carries the standard consumer flags plus MCP-specific fields. Vocabulary lives in [`utils/roles/applications/services/mcp.py`](../../../../../utils/roles/applications/services/mcp.py); the hard lint is [`tests/lint/ansible/services/test_mcp_schema.py`](../../../../../tests/lint/ansible/services/test_mcp_schema.py).
|
||||
A role declares `meta/mcp.yml` when it actually serves or consumes MCP. The file is a capability descriptor, not a service entry: no single role provides MCP, so the service registry cannot pair provider and consumer, and `allowed_consumers` does it instead. Vocabulary lives in [`utils/roles/applications/mcp.py`](../../../../../utils/roles/applications/mcp.py); the hard lint is [`test_mcp_schema.py`](../../../../../tests/lint/ansible/services/test_mcp_schema.py).
|
||||
|
||||
## Shape
|
||||
`classification` is required and is one of `native_server`, `native_client`, `native_both`, `plugin_server`, `sidecar_server`, `adapter_server`. A role that cannot serve declares no `mcp` block at all.
|
||||
|
||||
## Server shape
|
||||
|
||||
```yaml
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: false
|
||||
shared: false
|
||||
direction: server # server | client | both
|
||||
transport: streamable_http # streamable_http | sse
|
||||
exposure: internal # internal | public
|
||||
auth: bearer_token # oidc | app_password | basic_auth | bearer_token | upstream_session | none
|
||||
auth_subject: user # user | service_account | administrator | none
|
||||
implementation: native # native | plugin | sidecar | external
|
||||
endpoint: # required for direction: server | both
|
||||
service_key: baserow # names a service entry in the same file
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_server
|
||||
direction: server # server | client | both
|
||||
transport: sse # streamable_http | sse
|
||||
exposure: internal # internal | public
|
||||
auth: app_password # oidc | app_password | basic_auth | bearer_token | upstream_session | none
|
||||
auth_subject: service_account # user | service_account | administrator | none
|
||||
implementation: native # native | plugin | sidecar | adapter | external
|
||||
credential:
|
||||
owner: mcp-web-app-example # a dedicated non-login identity, never `administrator`
|
||||
source: token_store # token_store | credentials
|
||||
key: web-app-example
|
||||
allowed_consumers: # exact client application ids
|
||||
- web-app-openwebui
|
||||
endpoint:
|
||||
service_key: example # names a service entry in the same file
|
||||
path: /mcp
|
||||
port_key: http # resolves under that service's ports.local or ports.internal
|
||||
port_key: http # resolves under that service's ports.local or ports.internal
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
```
|
||||
|
||||
`exposure: public` additionally requires a complete `limits` block.
|
||||
|
||||
## Client shape
|
||||
|
||||
A client declares what it can present instead of what it serves:
|
||||
|
||||
```yaml
|
||||
mcp:
|
||||
classification: native_client
|
||||
direction: client
|
||||
implementation: native
|
||||
supported_transports: [streamable_http, sse]
|
||||
supported_auths: [bearer_token, app_password, upstream_session]
|
||||
```
|
||||
|
||||
A client-only block MUST NOT declare an `endpoint`.
|
||||
|
||||
## Adapter shape
|
||||
|
||||
`implementation: adapter` requires `classification: adapter_server` and a pinned, hash-checked contract:
|
||||
|
||||
```yaml
|
||||
adapter:
|
||||
type: openapi_allowlist # see below
|
||||
image: registry.example.invalid/infinito-mcp-adapter
|
||||
version: 1.2.3
|
||||
digest: sha256:0123456789abcdef
|
||||
upstream_api_version: v1
|
||||
specification_path: roles/web-app-example/files/mcp/openapi-v1.json
|
||||
specification_sha256: sha256:fedcba9876543210
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 500
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist: [example_search, example_get]
|
||||
schema_sha256: sha256:abcdef0123456789
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
```
|
||||
|
||||
`type` is one of `openapi_allowlist`, `graphql_allowlist`, `named_query`, `s3_prefix`, `prometheus_readonly`, `n8n_workflow`, `resource_readonly`. `graphql_allowlist`, `named_query`, `s3_prefix` and `resource_readonly` additionally require a non-empty `adapter.scope` naming the exact buckets, prefixes, collections or queries reachable. `openapi_allowlist` and `graphql_allowlist` require a checked-in `specification_path` under the role's own `files/`.
|
||||
|
||||
## Rules
|
||||
|
||||
- `direction` is required; all MCP-specific fields MUST be literals (only `enabled`/`shared` may carry Jinja).
|
||||
- `direction: server | both` requires `auth` and a complete `endpoint` (`service_key`, `path`, `port_key`).
|
||||
- `classification` is required and MUST be deployable; all MCP-specific fields MUST be literals (only `enabled`/`shared` may carry Jinja).
|
||||
- `enabled`/`shared` MUST NOT name a peer role. Provider enablement is explicit operator or variant state; the variant axis lives in `meta/variants.yml`.
|
||||
- `direction: server | both` requires `auth`, `credential`, `allowed_consumers` and a complete `endpoint`.
|
||||
- `credential.owner` MUST NOT be `administrator`. Two providers resolving to the same owner and secret abort discovery.
|
||||
- `auth: none` requires `exposure: internal`.
|
||||
- `auth_subject: service_account | administrator` requires an explicit `tools.mutating_tools_enabled: false`; omitting the key is rejected, because a privileged subject must state its tool policy rather than inherit a default. The field records the intent; where upstream exposes mutating tools unconditionally, the role README names the missing enforcement.
|
||||
- `auth_subject: service_account | administrator` requires an explicit `tools.mutating_tools_enabled: false`.
|
||||
- `auth_subject: user` or `auth: oidc` requires a `delegation` block; see [mcp-delegation.md](mcp-delegation.md).
|
||||
- `endpoint.service_key` MUST name a service in the same file; `endpoint.port_key` MUST resolve under its `ports.local` or `ports.internal`.
|
||||
- Suppress a finding with `# nocheck: mcp-schema` (file head = whole file, on/above a line = that finding).
|
||||
|
||||
## Public exposure
|
||||
|
||||
`exposure: public` puts the endpoint on the role's own vhost, so it inherits
|
||||
what that vhost already provides: routing through the shared proxy, TLS from the
|
||||
platform certificate, `client_max_body_size` from `server.client_max_body_size`,
|
||||
and the proxy read timeout of the location it is mounted in.
|
||||
`exposure: public` puts the endpoint on the role's own vhost, so it inherits what that vhost already provides: routing through the shared proxy, TLS from the platform certificate, `client_max_body_size` from `server.client_max_body_size`, and the proxy read timeout of the location it is mounted in.
|
||||
|
||||
Rate limiting is not among them. The proxy ships no `limit_req` or `limit_conn`
|
||||
anywhere, so a public MCP endpoint is only as protected as its own
|
||||
authentication. An operator exposing one to the open internet has to put a rate
|
||||
limiter in front of the vhost; the platform will not supply one.
|
||||
Rate limiting is not among them. The proxy ships no `limit_req` or `limit_conn` anywhere, so a public MCP endpoint is only as protected as its own authentication and its declared `limits`. An operator exposing one to the open internet has to put a rate limiter in front of the vhost; the platform will not supply one.
|
||||
|
||||
Prefer `exposure: internal`. A client inside the container network reaches the
|
||||
endpoint without any of this, which is why five of the eight server roles stay
|
||||
internal and only those whose upstream mounts MCP under the application's own
|
||||
public routes are public.
|
||||
Prefer `exposure: internal`. A client inside the container network reaches the endpoint without any of this.
|
||||
|
||||
## Why stdio is not deployed
|
||||
|
||||
`transport` accepts `streamable_http` and `sse` only. A stdio MCP server is a
|
||||
local process the client spawns, so it inherits the client container's
|
||||
filesystem and network with no endpoint to authenticate, no proxy to route
|
||||
through, and nothing for a probe to reach. Every property the other rules rely
|
||||
on (`auth`, `exposure`, `endpoint`) is undefined for it. Clients that support
|
||||
stdio are pinned away from it in their own role: Flowise sets
|
||||
`CUSTOM_MCP_PROTOCOL=sse` so no flow can spawn a command.
|
||||
`transport` accepts `streamable_http` and `sse` only. A stdio MCP server is a local process the client spawns, so it inherits the client container's filesystem and network with no endpoint to authenticate, no proxy to route through, and nothing for a probe to reach. Every property the other rules rely on (`auth`, `exposure`, `endpoint`) is undefined for it. Clients that support stdio are pinned away from it in their own role: Flowise sets `CUSTOM_MCP_PROTOCOL=sse` so no flow can spawn a command.
|
||||
|
||||
## Client discovery
|
||||
|
||||
Client roles discover server-capable roles through the `roles_with_service` lookup with the `direction` kwarg:
|
||||
Clients do not read the server list directly. [`lookup('mcp_servers')`](../../../../../plugins/lookup/mcp_servers.py) computes the intersection of the provider's `allowed_consumers` with the calling client's `supported_transports` and `supported_auths`, resolves each provider's declared credential, and returns:
|
||||
|
||||
```yaml
|
||||
{{ lookup('roles_with_service', 'mcp', direction='server') }}
|
||||
MCP_DISCOVERY: "{{ lookup('mcp_servers') }}"
|
||||
MCP_DISCOVERED_SERVERS: "{{ MCP_DISCOVERY.selected }}"
|
||||
MCP_REJECTED_SERVERS: "{{ MCP_DISCOVERY.rejected }}"
|
||||
```
|
||||
|
||||
Each returned entry carries `id`, `canonical_domain`, `canonical_url`, `iframe`, plus `transport`, `auth`, `auth_subject` and `endpoint` (`service_key`, `path`, resolved `port`). Callers that omit `direction` keep the original 4-key entries.
|
||||
Every rejected entry carries a stable `reason`: `consumer_not_allowed`, `transport_unsupported`, `auth_unsupported`, `credential_missing` or `endpoint_unreachable`.
|
||||
|
||||
## RBAC
|
||||
|
||||
Reaching a tool server is a distinct grant from using the application, carried by the provider's `mcp` RBAC role. `mcp` is application-scoped: it is granted through `application_roles`, never through the unscoped `roles` list, so granting it on one application grants it nowhere else.
|
||||
|
||||
```yaml
|
||||
users:
|
||||
alice:
|
||||
application_roles:
|
||||
web-app-baserow: [mcp]
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- Service metadata basics: [base.md](base.md)
|
||||
- User delegation audit: [mcp-delegation.md](mcp-delegation.md)
|
||||
@@ -18,7 +18,7 @@ cannot be rendered, so they abort the run instead of disappearing quietly: a
|
||||
client that silently ends up with fewer tools than the deployment declared is
|
||||
indistinguishable from one that works.
|
||||
|
||||
The provider credential is whatever ``services.mcp.credential`` declares:
|
||||
The provider credential is whatever ``mcp.credential`` declares:
|
||||
``owner`` names the principal, ``source`` where its secret lives
|
||||
(``token_store`` or the role's own ``credentials``), ``key`` the entry. No
|
||||
provider inherits the administrator's token.
|
||||
@@ -32,7 +32,7 @@ from ansible.errors import AnsibleError
|
||||
from ansible.plugins.loader import lookup_loader
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
from utils.roles.applications.services.mcp import DEFAULT_MCP_TRANSPORT
|
||||
from utils.roles.applications.mcp import DEFAULT_MCP_TRANSPORT
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Mapping, Sequence
|
||||
@@ -113,7 +113,7 @@ def build_mcp_discovery(
|
||||
Args:
|
||||
servers: ``roles_with_service('mcp', direction='server')`` entries.
|
||||
consumer_id: ``application_id`` of the client doing the discovery.
|
||||
consumer: the client's own ``services.mcp`` block, carrying
|
||||
consumer: the client's own ``mcp`` block, carrying
|
||||
``supported_transports`` and ``supported_auths``.
|
||||
credentials: resolved ``(token, owner)`` per provider role id.
|
||||
path_keys: resolved ``key_credential`` values, keyed by role id.
|
||||
@@ -253,7 +253,13 @@ class LookupModule(LookupBase):
|
||||
|
||||
servers = lookup_loader.get(
|
||||
"roles_with_service", loader=self._loader, templar=templar
|
||||
).run(["mcp"], variables=vars_, direction="server", scope="deployment")[0]
|
||||
).run(
|
||||
["mcp"],
|
||||
variables=vars_,
|
||||
topic="mcp",
|
||||
direction="server",
|
||||
scope="deployment",
|
||||
)[0]
|
||||
|
||||
applications = lookup_loader.get(
|
||||
"applications", loader=self._loader, templar=templar
|
||||
@@ -262,9 +268,7 @@ class LookupModule(LookupBase):
|
||||
[], variables=vars_
|
||||
)[0]
|
||||
|
||||
consumer = ((applications.get(consumer_id) or {}).get("services") or {}).get(
|
||||
"mcp"
|
||||
) or {}
|
||||
consumer = (applications.get(consumer_id) or {}).get("mcp") or {}
|
||||
|
||||
credentials: dict[str, tuple[str, str]] = {}
|
||||
path_keys: dict[str, str] = {}
|
||||
|
||||
@@ -45,7 +45,7 @@ from ansible.plugins.loader import lookup_loader
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
|
||||
from utils.domains.primary_domain import get_primary_domain
|
||||
from utils.roles.applications.services.mcp import DEFAULT_MCP_TRANSPORT
|
||||
from utils.roles.applications.mcp import DEFAULT_MCP_TRANSPORT
|
||||
from utils.roles.entity.name import get_entity_name
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -125,6 +125,7 @@ class LookupModule(LookupBase):
|
||||
).run([], variables=vars_)[0]
|
||||
|
||||
scope = str(kwargs.get("scope", "host")).strip().lower()
|
||||
topic = kwargs.get("topic")
|
||||
direction_raw = kwargs.get("direction")
|
||||
direction = (
|
||||
str(direction_raw).strip().lower() if direction_raw is not None else None
|
||||
@@ -141,8 +142,8 @@ class LookupModule(LookupBase):
|
||||
continue
|
||||
services = app_config.get("services")
|
||||
if not isinstance(services, dict):
|
||||
continue
|
||||
block = services.get(service_name)
|
||||
services = {}
|
||||
block = app_config.get(topic) if topic else services.get(service_name)
|
||||
if not isinstance(block, dict):
|
||||
continue
|
||||
if not bool(block.get("enabled")):
|
||||
|
||||
@@ -23,6 +23,7 @@ flowchart LR
|
||||
end
|
||||
subgraph role [svc-db-qdrant 🐳🐝]
|
||||
svc_qdrant["qdrant"]
|
||||
svc_qdrantmcp["qdrantmcp"]
|
||||
svc_container_backup["container_backup"]
|
||||
end
|
||||
subgraph dependents [Dependents]
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
enabled: false
|
||||
shared: false
|
||||
classification: adapter_server
|
||||
direction: server
|
||||
source_url: https://s.infinito.nexus/code
|
||||
supported_version: v1.18.2
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: adapter
|
||||
credential:
|
||||
owner: mcp-svc-db-qdrant
|
||||
source: credentials
|
||||
key: mcp_bearer
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: qdrantmcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
adapter:
|
||||
type: openapi_allowlist
|
||||
image: infinito/mcp-adapter
|
||||
version: 1.0.0
|
||||
digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
|
||||
upstream_api_version: v1
|
||||
specification_path: roles/svc-db-qdrant/files/mcp/tools.json
|
||||
specification_sha256: sha256:95037a1f1d060f8bbf092ac5943fb705d699ce0c30491a4b1c884fe1bf81d2f7
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 100
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- qdrant_list_collections
|
||||
- qdrant_get_collection
|
||||
schema_sha256: sha256:95037a1f1d060f8bbf092ac5943fb705d699ce0c30491a4b1c884fe1bf81d2f7
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -26,6 +26,24 @@ qdrant:
|
||||
internal:
|
||||
qdrant: 6333
|
||||
lifecycle: beta
|
||||
# nocheck: playwright-service-flag internal adapter sidecar, covered by the role's MCP integration test
|
||||
# nocheck: dockerfile-custom the Dockerfile is owned by svc-ai-mcp-adapter and staged per instance, so every adapter-backed provider shares one audited build
|
||||
# nocheck: dynamic-flag the sidecar tracks this role's own mcp flag, not another role's presence; a group_names expression would decide its exposure from an unrelated deployment
|
||||
qdrantmcp:
|
||||
enabled: "{{ lookup('config', 'svc-db-qdrant', 'mcp.enabled') | bool }}"
|
||||
shared: false
|
||||
image: python
|
||||
# nocheck: docker-version base of the self-built stdlib adapter, not the served application
|
||||
version: "3.13-slim"
|
||||
custom: true
|
||||
name: qdrantmcp
|
||||
cpus: "0.25"
|
||||
mem_reservation: 24m
|
||||
mem_limit: 48m
|
||||
pids_limit: 64
|
||||
ports:
|
||||
internal:
|
||||
http: 8080
|
||||
|
||||
# nocheck: playwright-service-flag, dynamic-flag infrastructure backup consumer
|
||||
container_backup: # nocheck: bond-baseline backup host joins outside the variant topology
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
# Matrix-deploy variant overrides for svc-db-qdrant.
|
||||
# See docs/contributing/artefact/files/role/variants.md for the file format.
|
||||
|
||||
# variant 0: MCP adapter enabled (canonical default).
|
||||
|
||||
- mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -22,7 +22,6 @@ flowchart LR
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_mailu["web-app-mailu 🐳🐝"]
|
||||
dep_web_app_matomo["web-app-matomo 🐳🐝"]
|
||||
dep_web_app_openwebui["web-app-openwebui 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
dep_web_app_seaweedfs["web-app-seaweedfs 🐳🐝"]
|
||||
dep_web_svc_css["web-svc-css 💻"]
|
||||
@@ -33,7 +32,6 @@ flowchart LR
|
||||
svc_dashboard["dashboard"]
|
||||
svc_matomo["matomo"]
|
||||
svc_email["email"]
|
||||
svc_mcp["mcp"]
|
||||
svc_sso["sso"]
|
||||
svc_redis["redis"]
|
||||
svc_postgres["postgres"]
|
||||
@@ -56,7 +54,6 @@ flowchart LR
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_openwebui -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_app_seaweedfs -. "0..1" .-> svc_seaweedfs
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
@@ -148,7 +145,7 @@ Configuration is controlled via `applications.<app>.bootstrap_admin.*`:
|
||||
## MCP Server
|
||||
|
||||
Baserow ships a native MCP server in the OSS backend, mounted on the ASGI root
|
||||
ahead of Django. The role turns it on with `services.mcp.enabled` and hands
|
||||
ahead of Django. The role turns it on with `mcp.enabled` and hands
|
||||
clients the container-network origin (`exposure: internal`); `env.j2` then adds
|
||||
that origin to `BASEROW_EXTRA_PUBLIC_URLS` so the image's Caddy layer routes
|
||||
`/mcp/*` to the backend instead of falling through to the Nuxt frontend.
|
||||
@@ -190,7 +187,7 @@ Read tools: `list_databases`, `list_tables`, `get_table_schema`,
|
||||
|
||||
Write tools: `create_rows`, `update_rows`, `delete_rows`. Baserow 2.3.1 exposes
|
||||
these unconditionally; the image carries no env var, setting or UI toggle that
|
||||
removes them. `services.mcp.tools.mutating_tools_enabled` is declared `false` as
|
||||
removes them. `mcp.tools.mutating_tools_enabled` is declared `false` as
|
||||
the deployment's intent, not as an enforced state: Baserow serves the three write
|
||||
tools regardless. What bounds them is the endpoint owner, whose reach is the one
|
||||
workspace that account owns.
|
||||
@@ -209,12 +206,12 @@ the role's `mcp` RBAC group, not on being signed in.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is true only while `web-app-openwebui` is part of the
|
||||
Off. `mcp.enabled` is true only while `web-app-openwebui` is part of the
|
||||
deployment.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client role, or pin `services.mcp.enabled: false` for this role in
|
||||
Remove the MCP client role, or pin `mcp.enabled: false` for this role in
|
||||
the inventory. The endpoint is then not provisioned and `BASEROW_EXTRA_PUBLIC_URLS`
|
||||
drops the MCP origin, so the image's Caddy layer stops routing `/mcp/*`.
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_server
|
||||
direction: server
|
||||
source_url: https://baserow.io/user-docs/mcp-server # nocheck: url — page is live (HEAD+GET 200); baserow.io edge intermittently 404s CI runner IPs
|
||||
supported_version: 2.3.1
|
||||
minimum_version: "1.30"
|
||||
transport: sse
|
||||
exposure: internal
|
||||
auth: app_password
|
||||
auth_subject: service_account
|
||||
implementation: native
|
||||
credential:
|
||||
owner: mcp-web-app-baserow
|
||||
source: token_store
|
||||
key: web-app-baserow
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
- web-app-n8n
|
||||
endpoint:
|
||||
service_key: baserow
|
||||
path: /mcp
|
||||
port_key: http
|
||||
key_credential: mcp_endpoint_key
|
||||
suffix: sse
|
||||
tools:
|
||||
allowlist:
|
||||
- create_rows
|
||||
- delete_rows
|
||||
- get_table_schema
|
||||
- list_databases
|
||||
- list_table_rows
|
||||
- list_tables
|
||||
- update_rows
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -15,29 +15,6 @@ email:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-mailu' in group_names }}"
|
||||
shared: "{{ 'web-app-mailu' in group_names }}"
|
||||
# nocheck: shared-provider
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-openwebui' in group_names }}"
|
||||
shared: "{{ 'web-app-openwebui' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://baserow.io/user-docs/mcp-server # nocheck: url — page is live (HEAD+GET 200); baserow.io edge intermittently 404s CI runner IPs
|
||||
minimum_version: "1.30"
|
||||
notes: Native built-in MCP server.
|
||||
transport: sse
|
||||
exposure: internal
|
||||
auth: app_password
|
||||
auth_subject: service_account
|
||||
implementation: native
|
||||
endpoint:
|
||||
service_key: baserow
|
||||
path: /mcp
|
||||
port_key: http
|
||||
key_credential: mcp_endpoint_key
|
||||
suffix: sse
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
sso:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-keycloak' in group_names }}"
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
matomo:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
sso:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -38,6 +35,9 @@
|
||||
css:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -54,9 +54,6 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
@@ -73,3 +70,6 @@
|
||||
css:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -21,7 +21,7 @@ MATOMO_BASE_URL={{ lookup('tls', 'web-app-matomo', 'url.base') | dotenv_quote }}
|
||||
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ BASEROW_MANAGE_PY: "/baserow/backend/src/baserow/manage.py"
|
||||
BASEROW_SERVICE_NAME: "baserow"
|
||||
BASEROW_INTERNAL_URL: "http://{{ BASEROW_CONTAINER }}:{{ lookup('config', application_id, 'services.baserow.ports.internal.http') }}"
|
||||
|
||||
BASEROW_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
BASEROW_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
BASEROW_MCP_ENDPOINT_KEY: "{{ lookup('config', application_id, 'credentials.mcp_endpoint_key') }}"
|
||||
BASEROW_MCP_ENDPOINT_PATH: "{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}"
|
||||
BASEROW_MCP_ENDPOINT_PATH: "{{ lookup('config', application_id, 'mcp.endpoint.path') }}"
|
||||
BASEROW_MCP_ENDPOINT_NAME: "Infinito.Nexus"
|
||||
BASEROW_MCP_WORKSPACE_NAME: "Infinito.Nexus MCP"
|
||||
BASEROW_MCP_OWNER_EMAIL: "mcp@{{ lookup('domain', application_id) }}"
|
||||
|
||||
@@ -37,6 +37,7 @@ flowchart LR
|
||||
svc_prometheus["prometheus"]
|
||||
svc_checkmk["checkmk"]
|
||||
svc_container_backup["container_backup"]
|
||||
svc_checkmkmcp["checkmkmcp"]
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_svc_db_openldap -. "0..1" .-> svc_ldap
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
enabled: false
|
||||
shared: false
|
||||
classification: adapter_server
|
||||
direction: server
|
||||
source_url: https://s.infinito.nexus/code
|
||||
supported_version: 1.0.0
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: adapter
|
||||
credential:
|
||||
owner: mcp-web-app-checkmk
|
||||
source: credentials
|
||||
key: mcp_bearer
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: checkmkmcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
adapter:
|
||||
type: openapi_allowlist
|
||||
image: infinito/mcp-adapter
|
||||
version: 1.0.0
|
||||
digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
|
||||
upstream_api_version: v1
|
||||
specification_path: roles/web-app-checkmk/files/mcp/tools.json
|
||||
specification_sha256: sha256:9a769b4524a2c55a12e06c6fb3d906cff1ccd7f1f3445e3543a94891a8d466be
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 100
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- checkmk_get_host_status
|
||||
- checkmk_list_hosts
|
||||
- checkmk_list_services
|
||||
schema_sha256: sha256:9a769b4524a2c55a12e06c6fb3d906cff1ccd7f1f3445e3543a94891a8d466be
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -88,3 +88,18 @@ container_backup: # nocheck: bond-baseline backup host joins outside the variant
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
|
||||
shared: true
|
||||
# nocheck: playwright-service-flag internal adapter sidecar, covered by the role's MCP guest spec
|
||||
# nocheck: dockerfile-custom the Dockerfile is owned by svc-ai-mcp-adapter and staged per instance, so every adapter-backed provider shares one audited build
|
||||
checkmkmcp:
|
||||
image: python
|
||||
# nocheck: docker-version base of the self-built stdlib adapter, not the served application
|
||||
version: "3.13-slim"
|
||||
custom: true
|
||||
name: checkmkmcp
|
||||
cpus: "0.25"
|
||||
mem_reservation: 16m
|
||||
mem_limit: 24m
|
||||
pids_limit: 64
|
||||
ports:
|
||||
internal:
|
||||
http: 8080
|
||||
@@ -27,6 +27,9 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -56,6 +59,9 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -84,3 +90,6 @@
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -36,7 +36,6 @@ flowchart LR
|
||||
svc_css["css"]
|
||||
svc_prometheus["prometheus"]
|
||||
svc_container_backup["container_backup"]
|
||||
svc_mcp["mcp ❌"]
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_svc_db_postgres -. "0..1" .-> svc_postgres
|
||||
|
||||
@@ -69,11 +69,3 @@ container_backup: # nocheck: bond-baseline backup host joins outside the variant
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
|
||||
shared: true
|
||||
|
||||
# nocheck: dynamic-flag a blocker block can never be enabled, so a group_names expression would be dead code
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
blocker: hosted_only
|
||||
source_url: https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/
|
||||
notes: Rovo MCP is Atlassian Cloud only; this role deploys an unsupported self-hosted container.
|
||||
@@ -42,7 +42,6 @@ flowchart LR
|
||||
svc_css["css"]
|
||||
svc_prometheus["prometheus"]
|
||||
svc_container_backup["container_backup"]
|
||||
svc_mcp["mcp ❌"]
|
||||
end
|
||||
subgraph dependents [Dependents]
|
||||
dpt_web_app_nextcloud["web-app-nextcloud 🐳🐝"]
|
||||
|
||||
@@ -104,12 +104,3 @@ container_backup: # nocheck: bond-baseline backup host joins outside the variant
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
|
||||
shared: true
|
||||
|
||||
# nocheck: dynamic-flag a blocker block can never be enabled, so a group_names expression would be dead code
|
||||
# nocheck: playwright-service-flag no MCP surface is served, so no spec can exercise one
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
blocker: hosted_only
|
||||
source_url: https://meta.discourse.org/
|
||||
notes: An MCP repository exists, but no stable supported server contract has been confirmed.
|
||||
@@ -48,6 +48,7 @@ flowchart LR
|
||||
svc_javascript["javascript"]
|
||||
svc_prometheus["prometheus"]
|
||||
svc_seaweedfs["seaweedfs"]
|
||||
svc_fidermcp["fidermcp"]
|
||||
end
|
||||
dep_svc_db_postgres -. "0..1" .-> svc_postgres
|
||||
dep_svc_db_redis -. "0..1" .-> svc_redis
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
enabled: false
|
||||
shared: false
|
||||
classification: adapter_server
|
||||
direction: server
|
||||
source_url: https://s.infinito.nexus/code
|
||||
supported_version: 1.0.0
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: adapter
|
||||
credential:
|
||||
owner: mcp-web-app-fider
|
||||
source: credentials
|
||||
key: mcp_bearer
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: fidermcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
adapter:
|
||||
type: openapi_allowlist
|
||||
image: infinito/mcp-adapter
|
||||
version: 1.0.0
|
||||
digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
|
||||
upstream_api_version: v1
|
||||
specification_path: roles/web-app-fider/files/mcp/tools.json
|
||||
specification_sha256: sha256:50c9683dcad2b66b3a1c07b87fb553bdf5543103cfccb75522374362528fb524
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 100
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- fider_get_suggestion
|
||||
- fider_list_tags
|
||||
- fider_search_suggestions
|
||||
schema_sha256: sha256:50c9683dcad2b66b3a1c07b87fb553bdf5543103cfccb75522374362528fb524
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -74,3 +74,18 @@ seaweedfs:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-seaweedfs' in group_names }}"
|
||||
shared: "{{ 'web-app-seaweedfs' in group_names }}"
|
||||
# nocheck: playwright-service-flag internal adapter sidecar, covered by the role's MCP guest spec
|
||||
# nocheck: dockerfile-custom the Dockerfile is owned by svc-ai-mcp-adapter and staged per instance, so every adapter-backed provider shares one audited build
|
||||
fidermcp:
|
||||
image: python
|
||||
# nocheck: docker-version base of the self-built stdlib adapter, not the served application
|
||||
version: "3.13-slim"
|
||||
custom: true
|
||||
name: fidermcp
|
||||
cpus: "0.25"
|
||||
mem_reservation: 16m
|
||||
mem_limit: 24m
|
||||
pids_limit: 64
|
||||
ports:
|
||||
internal:
|
||||
http: 8080
|
||||
@@ -33,6 +33,9 @@
|
||||
seaweedfs:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
dashboard:
|
||||
enabled: false
|
||||
@@ -62,3 +65,6 @@
|
||||
seaweedfs:
|
||||
enabled: false
|
||||
shared: false
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -39,7 +39,6 @@ flowchart LR
|
||||
svc_sso["sso"]
|
||||
svc_ldap["ldap ❌"]
|
||||
svc_litellm["litellm"]
|
||||
svc_mcp["mcp"]
|
||||
svc_qdrant["qdrant"]
|
||||
svc_postgres["postgres"]
|
||||
svc_flowise["flowise"]
|
||||
@@ -61,10 +60,8 @@ flowchart LR
|
||||
dep_svc_db_qdrant -. "0..1" .-> svc_qdrant
|
||||
dep_svc_db_redis -. "0..1" .-> svc_redis
|
||||
dep_web_app_baserow -. "0..1" .-> svc_baserow
|
||||
dep_web_app_baserow -. "0..1" .-> svc_mcp
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_homeassistant -. "0..1" .-> svc_homeassistant
|
||||
dep_web_app_homeassistant -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
@@ -157,12 +154,12 @@ is a deliberate operator step.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is false unless an MCP server role is part of the
|
||||
Off. `mcp.enabled` is false unless an MCP server role is part of the
|
||||
deployment.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP server roles, or pin `services.mcp.enabled: false` for this role.
|
||||
Remove the MCP server roles, or pin `mcp.enabled: false` for this role.
|
||||
The security-check and deny-list overrides are then not rendered.
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_client
|
||||
direction: client
|
||||
source_url: https://github.com/FlowiseAI/Flowise/tree/flowise%403.1.4/packages/server/src/routes/custom-mcp-servers
|
||||
supported_version: 3.1.4
|
||||
minimum_version: 3.0.0
|
||||
implementation: native
|
||||
supported_transports:
|
||||
- streamable_http
|
||||
supported_auths:
|
||||
- bearer_token
|
||||
- app_password
|
||||
- upstream_session
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -32,23 +32,6 @@ litellm:
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-ai-litellm' in group_names }}"
|
||||
shared: "{{ 'svc-ai-litellm' in group_names }}"
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-homeassistant' in group_names or 'web-app-baserow' in group_names }}"
|
||||
shared: "{{ 'web-app-homeassistant' in group_names or 'web-app-baserow' in group_names }}"
|
||||
direction: client
|
||||
source_url: https://docs.flowiseai.com/tutorials/tools-and-mcp
|
||||
minimum_version: 3.0.0
|
||||
notes: Custom MCP tool node, configured per flow; no instance-level registry API, so servers cannot be preregistered. Arbitrary stdio commands stay off in
|
||||
deployments.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: user
|
||||
implementation: native
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: playwright-service-flag - DB engine, covered by role-local integration tests
|
||||
qdrant:
|
||||
bond: 1
|
||||
@@ -71,10 +54,15 @@ flowise:
|
||||
shared: true
|
||||
backup:
|
||||
no_stop_required: false
|
||||
image: flowiseai/flowise
|
||||
version: 3.1.3
|
||||
image: node
|
||||
# nocheck: docker-version
|
||||
version: "24-bookworm-slim"
|
||||
npm_version: 3.1.4
|
||||
sqlite_version: 12.11.1
|
||||
custom: true
|
||||
name: flowise
|
||||
min_storage: 1195MB
|
||||
# nocheck: min-storage-headroom Reason: the deployed image is built here from node:24-bookworm-slim and measures 4.8 GB, so the base image transfer is not its footprint
|
||||
min_storage: 6GB
|
||||
cpus: "1.0"
|
||||
mem_reservation: 1g
|
||||
mem_limit: 2g
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
litellm:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
qdrant:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -51,6 +48,9 @@
|
||||
baserow:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -76,9 +76,6 @@
|
||||
litellm:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
qdrant:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
@@ -98,3 +95,6 @@
|
||||
baserow:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -5,6 +5,10 @@ http_port: "{{ lookup('config', application_id, 'services.flowise.ports.local.ht
|
||||
|
||||
FLOWISE_CONTAINER: "{{ lookup('config', application_id, 'services.flowise.name') }}"
|
||||
FLOWISE_PORT_INTERNAL: 3000
|
||||
FLOWISE_NODE_MODULES: /opt/flowise/node_modules
|
||||
FLOWISE_BUILD_DIR: "{{ [lookup('container', application_id, 'directories.instance'), 'build'] | path_join }}"
|
||||
FLOWISE_NPM_VERSION: "{{ lookup('config', application_id, 'services.flowise.npm_version') }}"
|
||||
FLOWISE_SQLITE_VERSION: "{{ lookup('config', application_id, 'services.flowise.sqlite_version') }}"
|
||||
|
||||
FLOWISE_LDAP_ENABLED: "{{ lookup('config', application_id, 'services.ldap.enabled') }}"
|
||||
|
||||
@@ -13,7 +17,7 @@ FLOWISE_LITELLM_API_KEY: "{{ lookup('config', application_id, 'credentials.litel
|
||||
|
||||
FLOWISE_QDRANT_INTERNAL_URL: "{{ lookup('engine', 'qdrant', application_id, 'url') }}"
|
||||
|
||||
FLOWISE_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
FLOWISE_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
FLOWISE_MCP_SERVERS: >-
|
||||
{%- set out = [] -%}
|
||||
{%- if FLOWISE_MCP_ENABLED | bool -%}
|
||||
@@ -22,3 +26,19 @@ FLOWISE_MCP_SERVERS: >-
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
{{ out }}
|
||||
FLOWISE_API_BOT_USER: "{{ lookup('users', 'flowise-api-bot') }}"
|
||||
|
||||
FLOWISE_API_BOT_PASSWORD: "{{ lookup('config', application_id, 'credentials.api_bot_password') }}"
|
||||
FLOWISE_MCP_API_KEY_NAME: "infinito:mcp-registry"
|
||||
FLOWISE_MCP_DESIRED: >-
|
||||
{%- set out = [] -%}
|
||||
{%- for server in FLOWISE_MCP_SERVERS -%}
|
||||
{%- set _ = out.append({
|
||||
'id': server.id,
|
||||
'url': server.url,
|
||||
'transport': server.transport | replace('-', '_'),
|
||||
'header': 'Authorization',
|
||||
'token': server | mcp_authorization,
|
||||
'tools': []}) -%}
|
||||
{%- endfor -%}
|
||||
{{ out }}
|
||||
@@ -20,11 +20,9 @@ flowchart LR
|
||||
dep_svc_db_openldap["svc-db-openldap 🐳🐝"]
|
||||
dep_svc_db_redis["svc-db-redis 🐳🐝"]
|
||||
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
|
||||
dep_web_app_hermes["web-app-hermes 🐳🐝"]
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_mailu["web-app-mailu 🐳🐝"]
|
||||
dep_web_app_matomo["web-app-matomo 🐳🐝"]
|
||||
dep_web_app_openclaw["web-app-openclaw 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
dep_web_app_seaweedfs["web-app-seaweedfs 🐳🐝"]
|
||||
dep_web_svc_css["web-svc-css 💻"]
|
||||
@@ -40,7 +38,6 @@ flowchart LR
|
||||
svc_mariadb["mariadb"]
|
||||
svc_gitea["gitea"]
|
||||
svc_giteamcp["giteamcp"]
|
||||
svc_mcp["mcp"]
|
||||
svc_redis["redis"]
|
||||
svc_minio["minio ❌"]
|
||||
svc_seaweedfs["seaweedfs"]
|
||||
@@ -53,11 +50,9 @@ flowchart LR
|
||||
dep_svc_db_openldap -. "0..1" .-> svc_ldap
|
||||
dep_svc_db_redis -. "0..1" .-> svc_redis
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_app_seaweedfs -. "0..1" .-> svc_seaweedfs
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
@@ -155,7 +150,7 @@ repository, issue, pull request, search, file, branch, tag, commit and release.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is false unless `web-app-hermes` or
|
||||
Off. `mcp.enabled` is false unless `web-app-hermes` or
|
||||
`web-app-openclaw` is deployed alongside.
|
||||
|
||||
### Public vhost
|
||||
@@ -166,7 +161,7 @@ vhost is answered with an SSO redirect, never with an MCP response.
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles from the deployment, or pin
|
||||
`services.mcp.enabled: false` for this role. The `giteamcp` service is then not
|
||||
`mcp.enabled: false` for this role. The `giteamcp` service is then not
|
||||
rendered into the compose file.
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: sidecar_server
|
||||
direction: server
|
||||
source_url: https://pkg.go.dev/gitea.com/gitea/gitea-mcp
|
||||
minimum_version: "1.24"
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: sidecar
|
||||
credential:
|
||||
owner: mcp-web-app-gitea
|
||||
source: token_store
|
||||
key: web-app-gitea
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: giteamcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
tools:
|
||||
allowlist:
|
||||
- attachment_read
|
||||
- get_commit
|
||||
- get_dir_contents
|
||||
- get_file_contents
|
||||
- get_latest_release
|
||||
- get_me
|
||||
- get_release
|
||||
- get_repository_tree
|
||||
- get_tag
|
||||
- get_user_orgs
|
||||
- issue_read
|
||||
- list_branches
|
||||
- list_commits
|
||||
- list_issues
|
||||
- list_my_repos
|
||||
- list_org_repos
|
||||
- list_pull_requests
|
||||
- list_releases
|
||||
- list_tags
|
||||
- pull_request_read
|
||||
- search_issues
|
||||
- search_org_teams
|
||||
- search_repos
|
||||
- search_users
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -93,26 +93,6 @@ giteamcp:
|
||||
ports:
|
||||
internal:
|
||||
http: 8080
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://pkg.go.dev/gitea.com/gitea/gitea-mcp
|
||||
minimum_version: "1.24"
|
||||
notes: Project-owned package; packaging and auth maturity gate it.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: administrator
|
||||
implementation: sidecar
|
||||
endpoint:
|
||||
service_key: giteamcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: dynamic-flag
|
||||
# nocheck: playwright-service-flag — DB engine, covered by role-local integration tests
|
||||
redis:
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
matomo:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
sso:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -41,6 +38,9 @@
|
||||
css:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -64,9 +64,6 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
@@ -79,6 +76,9 @@
|
||||
css:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -104,12 +104,12 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
prometheus:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
seaweedfs:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -24,7 +24,7 @@ MATOMO_BASE_URL={{ lookup('tls', 'web-app-matomo', 'url.base') | dotenv_quote }}
|
||||
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
PERSONA_ADMINISTRATOR_BLOCKED=true
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ GITEA_REDIS_PASSWORD: "{{ lookup('engine', 'redis', application_id, 'password')
|
||||
|
||||
GITEA_INTERNAL_API_URL: "http://{{ entity_name }}:{{ lookup('config', application_id, 'services.gitea.ports.internal.http') }}/api/v1"
|
||||
|
||||
GITEA_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
GITEA_MCP_SERVICE_KEY: "{{ lookup('config', application_id, 'services.mcp.endpoint.service_key') }}"
|
||||
GITEA_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
GITEA_MCP_SERVICE_KEY: "{{ lookup('config', application_id, 'mcp.endpoint.service_key') }}"
|
||||
GITEA_MCP_CONTAINER: "{{ lookup('config', application_id, 'services.giteamcp.name') }}"
|
||||
GITEA_MCP_PORT: "{{ lookup('config', application_id, 'services.giteamcp.ports.internal.http') }}"
|
||||
GITEA_MCP_URL: "http://{{ GITEA_MCP_SERVICE_KEY }}:{{ GITEA_MCP_PORT }}{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}"
|
||||
GITEA_MCP_URL: "http://{{ GITEA_MCP_SERVICE_KEY }}:{{ GITEA_MCP_PORT }}{{ lookup('config', application_id, 'mcp.endpoint.path') }}"
|
||||
GITEA_MCP_UPSTREAM_URL: "http://{{ entity_name }}:{{ lookup('config', application_id, 'services.gitea.ports.internal.http') }}"
|
||||
GITEA_MCP_TOKEN_NAME: "gitea-mcp"
|
||||
GITEA_MCP_TOKEN_SCOPES: "read:repository,read:issue,read:user,read:organization,read:notification"
|
||||
|
||||
@@ -19,12 +19,9 @@ flowchart LR
|
||||
dep_svc_db_postgres["svc-db-postgres 🐳🐝"]
|
||||
dep_svc_db_redis["svc-db-redis 🐳🐝"]
|
||||
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
|
||||
dep_web_app_hermes["web-app-hermes 🐳🐝"]
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_mailu["web-app-mailu 🐳🐝"]
|
||||
dep_web_app_matomo["web-app-matomo 🐳🐝"]
|
||||
dep_web_app_openclaw["web-app-openclaw 🐳🐝"]
|
||||
dep_web_app_openwebui["web-app-openwebui 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
dep_web_app_seaweedfs["web-app-seaweedfs 🐳🐝"]
|
||||
dep_web_svc_css["web-svc-css 💻"]
|
||||
@@ -36,7 +33,6 @@ flowchart LR
|
||||
svc_dashboard["dashboard"]
|
||||
svc_matomo["matomo"]
|
||||
svc_email["email"]
|
||||
svc_mcp["mcp"]
|
||||
svc_redis["redis"]
|
||||
svc_postgres["postgres"]
|
||||
svc_gitlab["gitlab"]
|
||||
@@ -58,12 +54,9 @@ flowchart LR
|
||||
dep_svc_db_postgres -. "0..1" .-> svc_postgres
|
||||
dep_svc_db_redis -. "0..1" .-> svc_redis
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_openwebui -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_app_seaweedfs -. "0..1" .-> svc_seaweedfs
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
@@ -84,7 +77,7 @@ Solid `1:1` edges are fixed relationships; dashed `0..1` edges are conditional (
|
||||
|
||||
## MCP server
|
||||
|
||||
`services.mcp` declares the Model Context Protocol surface GitLab serves natively from its Rails API.
|
||||
`mcp` declares the Model Context Protocol surface GitLab serves natively from its Rails API.
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
@@ -92,13 +85,13 @@ Solid `1:1` edges are fixed relationships; dashed `0..1` edges are conditional (
|
||||
| Transport | streamable HTTP (JSON-RPC `initialize`, `tools/list`, `tools/call`) |
|
||||
| Auth | `Authorization: Bearer <token>` |
|
||||
| Token subject | the `root` account |
|
||||
| Default state | off; `services.mcp.enabled` turns on when `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is in the deployment |
|
||||
| Default state | off; `mcp.enabled` turns on when `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is in the deployment |
|
||||
|
||||
With the service enabled, `tasks/03_mcp.yml` reads the token stored for `administrator` under this role's id, probes it against the running instance with a JSON-RPC `initialize` call, mints a replacement through `gitlab-rails runner` when the stored token is missing or rejected, writes the fresh token back through `sys-token-store`, and fails the deploy when the re-probe is still rejected. The minted token is a personal access token carrying the `mcp` scope with a 364-day expiry. That scope is filtered out of the interactive token picker, so tokens for this endpoint are created programmatically.
|
||||
|
||||
The role attaches to the shared overlay declared in `meta/networks.yml` so client containers can reach the endpoint container-to-container; the overlay alias resolves to workhorse.
|
||||
|
||||
Tool categories exposed at the pinned version cover issues and work items, merge requests (including diffs and conflicts), pipelines and jobs, labels, project and group search, repository files and commits, and instance metadata. The set includes mutating tools (`create_merge_request`, `create_workitem_note`, `link_work_items`). GitLab enforces no server-side read-only mode: the `mcp` scope grants both read and create access, and the only restriction mechanism is the per-request `X-Gitlab-Enabled-Mcp-Server-Tools` header, which the clients in this repository do not send. `services.mcp.tools.read_only_default` and `services.mcp.tools.mutating_tools_enabled` are declarative metadata, not an enforced policy. Every tool call runs with the blast radius of the `root` account.
|
||||
Tool categories exposed at the pinned version cover issues and work items, merge requests (including diffs and conflicts), pipelines and jobs, labels, project and group search, repository files and commits, and instance metadata. The set includes mutating tools (`create_merge_request`, `create_workitem_note`, `link_work_items`). GitLab enforces no server-side read-only mode: the `mcp` scope grants both read and create access, and the only restriction mechanism is the per-request `X-Gitlab-Enabled-Mcp-Server-Tools` header, which the clients in this repository do not send. `mcp.tools.read_only_default` and `mcp.tools.mutating_tools_enabled` are declarative metadata, not an enforced policy. Every tool call runs with the blast radius of the `root` account.
|
||||
|
||||
A Playwright scenario asserts that an unauthenticated request to the endpoint is never answered with a 2xx.
|
||||
|
||||
@@ -108,11 +101,11 @@ A Playwright scenario asserts that an unauthenticated request to the endpoint is
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is true only while `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is part of the deployment. The endpoint additionally requires a Premium or Ultimate licence, so a Community deployment leaves it unreachable regardless of the flag.
|
||||
Off. `mcp.enabled` is true only while `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is part of the deployment. The endpoint additionally requires a Premium or Ultimate licence, so a Community deployment leaves it unreachable regardless of the flag.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles, or pin `services.mcp.enabled: false` for this role. The token is then neither minted nor stored, and the overlay attachment is dropped.
|
||||
Remove the MCP client roles, or pin `mcp.enabled: false` for this role. The token is then neither minted nor stored, and the overlay attachment is dropped.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_server
|
||||
direction: server
|
||||
source_url: https://docs.gitlab.com/user/gitlab_duo/model_context_protocol/mcp_server/
|
||||
minimum_version: "18.0"
|
||||
transport: streamable_http
|
||||
exposure: public
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: native
|
||||
credential:
|
||||
owner: mcp-web-app-gitlab
|
||||
source: token_store
|
||||
key: web-app-gitlab
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: gitlab
|
||||
path: /api/v4/mcp
|
||||
port_key: http
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 500
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- create_issue
|
||||
- create_merge_request
|
||||
- create_workitem_note
|
||||
- get_issue
|
||||
- get_job_log
|
||||
- get_mcp_server_version
|
||||
- get_merge_request
|
||||
- get_merge_request_commits
|
||||
- get_merge_request_conflicts
|
||||
- get_merge_request_diffs
|
||||
- get_merge_request_pipelines
|
||||
- get_pipeline_jobs
|
||||
- get_saved_view_work_items
|
||||
- get_work_item_types
|
||||
- get_workitem_notes
|
||||
- link_work_items
|
||||
- manage_pipeline
|
||||
- search
|
||||
- search_labels
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -22,26 +22,6 @@ email:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-mailu' in group_names }}"
|
||||
shared: "{{ 'web-app-mailu' in group_names }}"
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names or 'web-app-openwebui' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names or 'web-app-openwebui' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://docs.gitlab.com/user/gitlab_duo/model_context_protocol/mcp_server/
|
||||
minimum_version: "18.0"
|
||||
notes: Beta and tier-gated to Premium or Ultimate; operator-gated here.
|
||||
transport: streamable_http
|
||||
exposure: public
|
||||
auth: bearer_token
|
||||
auth_subject: administrator
|
||||
implementation: native
|
||||
endpoint:
|
||||
service_key: gitlab
|
||||
path: /api/v4/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: shared
|
||||
# nocheck: dynamic-flag
|
||||
# nocheck: playwright-service-flag
|
||||
@@ -71,7 +51,6 @@ gitlab:
|
||||
enabled: true # nocheck: dynamic-flag
|
||||
shared: true
|
||||
min_storage: 30GB
|
||||
# Exception: the entity runs no container of its own (webservice/sidekiq/workhorse/gitaly/shell/migrations carry the real budgets); these keys only satisfy the entity resource contract
|
||||
cpus: "0.1"
|
||||
mem_reservation: 128m
|
||||
mem_limit: 256m
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
matomo:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
sso:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -38,6 +35,9 @@
|
||||
seaweedfs:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -54,9 +54,6 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
@@ -73,3 +70,6 @@
|
||||
seaweedfs:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -16,9 +16,9 @@ MATOMO_BASE_URL={{ lookup('tls', 'web-app-matomo', 'url.base') | dotenv_quote }}
|
||||
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'services.mcp.endpoint.path') | dotenv_quote }}
|
||||
MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'mcp.endpoint.path') | dotenv_quote }}
|
||||
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
PERSONA_ADMINISTRATOR_BLOCKED=true
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ GITLAB_OIDC_REDIRECT_URI: "{{ (GITLAB_URL | regex_replace('/$', '')) ~ '/users/a
|
||||
GITLAB_INIT_ROOT_PASSWORD: "{{ lookup('users', 'administrator').password }}"
|
||||
GITLAB_ROOT_USERNAME: "root"
|
||||
|
||||
GITLAB_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
GITLAB_MCP_PATH: "{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}"
|
||||
GITLAB_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
GITLAB_MCP_PATH: "{{ lookup('config', application_id, 'mcp.endpoint.path') }}"
|
||||
GITLAB_MCP_TOKEN_NAME: "infinito-mcp"
|
||||
GITLAB_RAILS_BIN: "/srv/gitlab/bin/rails" # nocheck: literal-path CNG images ship no omnibus gitlab-rails wrapper
|
||||
GITLAB_MCP_TOKEN_LIFETIME_DAYS: 364
|
||||
|
||||
@@ -30,7 +30,6 @@ flowchart LR
|
||||
svc_sso["sso ❌"]
|
||||
svc_logout["logout ❌"]
|
||||
svc_litellm["litellm"]
|
||||
svc_mcp["mcp"]
|
||||
svc_dashboard["dashboard"]
|
||||
svc_matomo["matomo"]
|
||||
svc_css["css"]
|
||||
@@ -40,30 +39,17 @@ flowchart LR
|
||||
end
|
||||
subgraph dependents [Dependents]
|
||||
dpt_svc_ai_robot["svc-ai-robot 💻"]
|
||||
dpt_web_app_gitea["web-app-gitea 🐳🐝"]
|
||||
dpt_web_app_gitlab["web-app-gitlab 🐳🐝"]
|
||||
dpt_web_app_homeassistant["web-app-homeassistant 🐳🐝"]
|
||||
dpt_web_app_jenkins["web-app-jenkins 🐳🐝"]
|
||||
dpt_web_app_mattermost["web-app-mattermost 🐳🐝"]
|
||||
dpt_web_app_moodle["web-app-moodle 🐳🐝"]
|
||||
dpt_web_app_nextcloud["web-app-nextcloud 🐳🐝"]
|
||||
end
|
||||
dep_svc_ai_litellm -. "0..1" .-> svc_litellm
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_homeassistant -. "0..1" .-> svc_homeassistant
|
||||
dep_web_app_homeassistant -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -- "1:1" --> svc_sso
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
svc_hermes -. "0..1" .-> dpt_svc_ai_robot
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_gitea
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_gitlab
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_homeassistant
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_jenkins
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_mattermost
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_moodle
|
||||
svc_hermes -. "0..1" .-> dpt_web_app_nextcloud
|
||||
```
|
||||
|
||||
@@ -151,12 +137,12 @@ the MCP credentials refuses an unauthenticated caller.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is false unless an MCP server role is part of the
|
||||
Off. `mcp.enabled` is false unless an MCP server role is part of the
|
||||
deployment.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP server roles, or pin `services.mcp.enabled: false` for this role.
|
||||
Remove the MCP server roles, or pin `mcp.enabled: false` for this role.
|
||||
The config then renders with an empty `mcp_servers` map and the verification step
|
||||
is skipped.
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_client
|
||||
direction: client
|
||||
source_url: https://hermes-agent.nousresearch.com/
|
||||
supported_version: v2026.7.20
|
||||
minimum_version: 0.2.0
|
||||
implementation: native
|
||||
supported_transports:
|
||||
- streamable_http
|
||||
- sse
|
||||
supported_auths:
|
||||
- bearer_token
|
||||
- app_password
|
||||
- upstream_session
|
||||
- basic_auth
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -54,24 +54,6 @@ litellm:
|
||||
enabled: "{{ 'svc-ai-litellm' in group_names }}"
|
||||
shared: "{{ 'svc-ai-litellm' in group_names }}"
|
||||
|
||||
# nocheck: shared-provider
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-homeassistant' in group_names }}"
|
||||
shared: "{{ 'web-app-homeassistant' in group_names }}"
|
||||
direction: client
|
||||
source_url: https://hermes-agent.nousresearch.com/
|
||||
minimum_version: 0.2.0
|
||||
notes: MCP client since v0.2.0, server since v0.6.0.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: user
|
||||
implementation: native
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
|
||||
dashboard:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-dashboard' in group_names }}"
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
litellm:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
dashboard:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -26,6 +23,9 @@
|
||||
homeassistant:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -33,9 +33,6 @@
|
||||
litellm:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override variant pins a literal off-switch
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override variant pins a literal off-switch
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override variant pins a literal off-switch
|
||||
@@ -51,3 +48,6 @@
|
||||
homeassistant:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override variant pins a literal off-switch
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -23,6 +23,6 @@ LITELLM_SERVICE_ENABLED={{ lookup('config', application_id, 'services.litellm.en
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
# nocheck: playwright-service-gate
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
PERSONA_BIBER_BLOCKED=true
|
||||
@@ -10,4 +10,4 @@ HERMES_LITELLM_ENABLED: "{{ lookup('config', application_id, 'services.litellm.e
|
||||
HERMES_LITELLM_API_KEY: "{{ lookup('config', application_id, 'credentials.litellm_api_key') }}"
|
||||
HERMES_CONFIG_PATH_HOST: "{{ [ lookup('container', application_id, 'directories.config'), 'config.yaml' ] | path_join }}"
|
||||
HERMES_DOCKER_USER_ID: 10000
|
||||
HERMES_MCP_SERVERS: "{{ MCP_DISCOVERED_SERVERS if (lookup('config', application_id, 'services.mcp.enabled') | bool) else [] }}"
|
||||
HERMES_MCP_SERVERS: "{{ MCP_DISCOVERED_SERVERS if (lookup('config', application_id, 'mcp.enabled') | bool) else [] }}"
|
||||
@@ -17,14 +17,11 @@ flowchart LR
|
||||
subgraph deps [Dependencies]
|
||||
dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"]
|
||||
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
|
||||
dep_web_app_hermes["web-app-hermes 🐳🐝"]
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_openclaw["web-app-openclaw 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
end
|
||||
subgraph role [web-app-homeassistant 🐳🐝]
|
||||
svc_homeassistant["homeassistant"]
|
||||
svc_mcp["mcp"]
|
||||
svc_sso["sso ❌"]
|
||||
svc_logout["logout ❌"]
|
||||
svc_dashboard["dashboard"]
|
||||
@@ -39,9 +36,7 @@ flowchart LR
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -- "1:1" --> svc_sso
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
svc_homeassistant -. "0..1" .-> dpt_web_app_flowise
|
||||
svc_homeassistant -. "0..1" .-> dpt_web_app_hermes
|
||||
@@ -103,7 +98,7 @@ docker run --rm -it \
|
||||
## MCP Server
|
||||
|
||||
Home Assistant ships the MCP server as a native integration. The role enables it
|
||||
through `services.mcp.enabled`, which stays true only while an MCP client role is
|
||||
through `mcp.enabled`, which stays true only while an MCP client role is
|
||||
part of the deployment.
|
||||
|
||||
### Endpoint
|
||||
@@ -136,7 +131,7 @@ for the current state.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is false unless `web-app-hermes` or
|
||||
Off. `mcp.enabled` is false unless `web-app-hermes` or
|
||||
`web-app-openclaw` is deployed alongside.
|
||||
|
||||
### Tool scope
|
||||
@@ -148,7 +143,7 @@ or actuated through MCP even though the write tools are listed.
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles from the deployment, or pin
|
||||
`services.mcp.enabled: false` for this role in the inventory. The integration is
|
||||
`mcp.enabled: false` for this role in the inventory. The integration is
|
||||
then not configured and the endpoint stops answering.
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_server
|
||||
direction: server
|
||||
source_url: https://www.home-assistant.io/integrations/mcp_server/
|
||||
minimum_version: "2025.2"
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: native
|
||||
credential:
|
||||
owner: mcp-web-app-homeassistant
|
||||
source: token_store
|
||||
key: web-app-homeassistant
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: homeassistant
|
||||
path: /api/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
allowlist:
|
||||
- GetDateTime
|
||||
- GetLiveContext
|
||||
- HassBroadcast
|
||||
- HassCancelAllTimers
|
||||
- HassListAddItem
|
||||
- HassListCompleteItem
|
||||
- HassListRemoveItem
|
||||
- HassTurnOff
|
||||
- HassTurnOn
|
||||
- todo_get_items
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -30,28 +30,6 @@ homeassistant:
|
||||
proxy:
|
||||
service_key: homeassistant
|
||||
|
||||
# nocheck: playwright-service-flag
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://www.home-assistant.io/integrations/mcp_server/
|
||||
minimum_version: "2025.2"
|
||||
notes: Native mcp_server integration, Streamable HTTP under /api/mcp.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: administrator
|
||||
implementation: native
|
||||
endpoint:
|
||||
service_key: homeassistant
|
||||
path: /api/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
|
||||
# nocheck: sso, dynamic-flag, playwright-service-flag
|
||||
sso:
|
||||
enabled: false
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
dashboard:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -15,16 +12,19 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
prometheus:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -3,7 +3,7 @@ CANONICAL_DOMAIN={{ lookup('domain', application_id) | dotenv_quote }}
|
||||
HOMEASSISTANT_BASE_URL={{ lookup('tls', application_id, 'url.base') | dotenv_quote }}
|
||||
|
||||
# nocheck: playwright-service-gate
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
PERSONA_ADMINISTRATOR_BLOCKED=true
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ http_port: "{{ lookup('config', application_id, 'services.homeassistant.ports.lo
|
||||
HOMEASSISTANT_CONTAINER: "{{ lookup('config', application_id, 'services.homeassistant.name') }}"
|
||||
HOMEASSISTANT_CONFIG_PATH_HOST: "{{ [ lookup('container', application_id, 'directories.config'), 'configuration.yaml' ] | path_join }}"
|
||||
HOMEASSISTANT_PORT_INTERNAL: "{{ lookup('config', application_id, 'services.homeassistant.ports.internal.http') }}"
|
||||
HOMEASSISTANT_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
HOMEASSISTANT_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
HOMEASSISTANT_ADMIN_USERNAME: "{{ lookup('users', 'administrator').username }}"
|
||||
HOMEASSISTANT_ADMIN_PASSWORD: "{{ lookup('config', application_id, 'credentials.administrator_password') }}"
|
||||
HOMEASSISTANT_TOKEN_CLIENT_NAME: "infinito-mcp"
|
||||
@@ -34,6 +34,7 @@ flowchart LR
|
||||
svc_prometheus["prometheus"]
|
||||
svc_jellyfin["jellyfin"]
|
||||
svc_container_backup["container_backup"]
|
||||
svc_jellyfinmcp["jellyfinmcp"]
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_svc_db_openldap -. "0..1" .-> svc_ldap
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
enabled: false
|
||||
shared: false
|
||||
classification: adapter_server
|
||||
direction: server
|
||||
source_url: https://s.infinito.nexus/code
|
||||
supported_version: 1.0.0
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: adapter
|
||||
credential:
|
||||
owner: mcp-web-app-jellyfin
|
||||
source: credentials
|
||||
key: mcp_bearer
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: jellyfinmcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
adapter:
|
||||
type: openapi_allowlist
|
||||
image: infinito/mcp-adapter
|
||||
version: 1.0.0
|
||||
digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
|
||||
upstream_api_version: v1
|
||||
specification_path: roles/web-app-jellyfin/files/mcp/tools.json
|
||||
specification_sha256: sha256:08776fdcbed9f362d76e9954881a9184362da85b24e2e503f8259fd664fd85f5
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 100
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- jellyfin_get_item
|
||||
- jellyfin_get_playback_status
|
||||
- jellyfin_search_library
|
||||
schema_sha256: sha256:08776fdcbed9f362d76e9954881a9184362da85b24e2e503f8259fd664fd85f5
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -65,3 +65,18 @@ container_backup: # nocheck: bond-baseline backup host joins outside the variant
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
|
||||
shared: true
|
||||
# nocheck: playwright-service-flag internal adapter sidecar, covered by the role's MCP guest spec
|
||||
# nocheck: dockerfile-custom the Dockerfile is owned by svc-ai-mcp-adapter and staged per instance, so every adapter-backed provider shares one audited build
|
||||
jellyfinmcp:
|
||||
image: python
|
||||
# nocheck: docker-version base of the self-built stdlib adapter, not the served application
|
||||
version: "3.13-slim"
|
||||
custom: true
|
||||
name: jellyfinmcp
|
||||
cpus: "0.25"
|
||||
mem_reservation: 16m
|
||||
mem_limit: 24m
|
||||
pids_limit: 64
|
||||
ports:
|
||||
internal:
|
||||
http: 8080
|
||||
@@ -24,6 +24,9 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -50,6 +53,9 @@
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -75,3 +81,6 @@
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -18,10 +18,8 @@ flowchart LR
|
||||
dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"]
|
||||
dep_svc_db_openldap["svc-db-openldap 🐳🐝"]
|
||||
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
|
||||
dep_web_app_hermes["web-app-hermes 🐳🐝"]
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_matomo["web-app-matomo 🐳🐝"]
|
||||
dep_web_app_openclaw["web-app-openclaw 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
dep_web_svc_css["web-svc-css 💻"]
|
||||
dep_web_svc_logout["web-svc-logout 🐳🐝"]
|
||||
@@ -34,17 +32,14 @@ flowchart LR
|
||||
svc_sso["sso"]
|
||||
svc_ldap["ldap"]
|
||||
svc_jenkins["jenkins"]
|
||||
svc_mcp["mcp"]
|
||||
svc_css["css"]
|
||||
svc_container_backup["container_backup"]
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_svc_db_openldap -. "0..1" .-> svc_ldap
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
dep_web_svc_logout -. "0..1" .-> svc_logout
|
||||
@@ -76,7 +71,7 @@ The role exposes Jenkins as an MCP server through the [MCP Server plugin](https:
|
||||
| Subject | administrator |
|
||||
| Tools | read-only by default, mutating tools off |
|
||||
|
||||
Enable the surface by deploying `web-app-hermes` or `web-app-openclaw` alongside Jenkins, or by forcing `services.mcp.enabled` on. Open WebUI skips the server because it cannot present basic auth.
|
||||
Enable the surface by deploying `web-app-hermes` or `web-app-openclaw` alongside Jenkins, or by forcing `mcp.enabled` on. Open WebUI skips the server because it cannot present basic auth.
|
||||
|
||||
At boot, `files/mcp-api-token.groovy` runs from `init.groovy.d` and mints an API token named `infinito-mcp` for the administrator account, writing the plain value to `/var/jenkins_home/secrets/infinito-mcp.token`. `tasks/01_mcp.yml` then reads that value out of the container, persists it through `sys-token-store`, and hard-fails the deploy when the controller rejects it. `MCP_DISCOVERED_SERVERS` picks the token up from the store and the client roles build the header via the `mcp_authorization` filter.
|
||||
|
||||
@@ -92,7 +87,7 @@ curl -u "<administrator>:<apiToken>" \
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is true only while `web-app-hermes` or
|
||||
Off. `mcp.enabled` is true only while `web-app-hermes` or
|
||||
`web-app-openclaw` is part of the deployment.
|
||||
|
||||
### Authorization subject
|
||||
@@ -110,7 +105,7 @@ unauthenticated `tools/list` is refused and returns no tool inventory.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles, or pin `services.mcp.enabled: false` for this role.
|
||||
Remove the MCP client roles, or pin `mcp.enabled: false` for this role.
|
||||
The MCP Server plugin is then not installed and the boot hook mints no API token.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: plugin_server
|
||||
direction: server
|
||||
source_url: https://plugins.jenkins.io/mcp-server/
|
||||
minimum_version: "2.462"
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: basic_auth
|
||||
auth_subject: service_account
|
||||
implementation: plugin
|
||||
credential:
|
||||
owner: mcp-web-app-jenkins
|
||||
source: token_store
|
||||
key: web-app-jenkins
|
||||
allowed_consumers:
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: jenkins
|
||||
path: /mcp-server/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
allowlist:
|
||||
- findJobsWithScmUrl
|
||||
- getBuild
|
||||
- getBuildChangeSets
|
||||
- getBuildLog
|
||||
- getBuildScm
|
||||
- getFlakyFailures
|
||||
- getJob
|
||||
- getJobScm
|
||||
- getJobs
|
||||
- getQueueItem
|
||||
- getReplayScripts
|
||||
- getStatus
|
||||
- getTestResults
|
||||
- rebuildBuild
|
||||
- replayBuild
|
||||
- searchBuildLog
|
||||
- triggerBuild
|
||||
- updateBuild
|
||||
- whoAmI
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -54,26 +54,6 @@ jenkins:
|
||||
mem_reservation: 1g
|
||||
mem_limit: 2g
|
||||
pids_limit: 1024
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://plugins.jenkins.io/mcp-server/
|
||||
minimum_version: "2.462"
|
||||
notes: Plugin based; pinned like every other Jenkins plugin.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: basic_auth
|
||||
auth_subject: administrator
|
||||
implementation: plugin
|
||||
endpoint:
|
||||
service_key: jenkins
|
||||
path: /mcp-server/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: playwright-service-flag — css consumer, no Playwright surface (provider: web-svc-css)
|
||||
css:
|
||||
bond: 1
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
css:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -58,9 +58,9 @@
|
||||
css:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -86,6 +86,6 @@
|
||||
prometheus:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -20,7 +20,7 @@ MATOMO_BASE_URL={{ lookup('tls', 'web-app-matomo', 'url.base') | dotenv_quote }}
|
||||
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'services.mcp.endpoint.path') | dotenv_quote }}
|
||||
MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'mcp.endpoint.path') | dotenv_quote }}
|
||||
|
||||
MCP_SERVICE_ENABLED={{ JENKINS_MCP_ENABLED | string | lower }}
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ JENKINS_LDAP_ENABLED: "{{ lookup('config', application_id, 'services.ldap.enable
|
||||
|
||||
JENKINS_LOOPBACK_URL: "http://localhost:{{ lookup('config', application_id, 'services.jenkins.ports.internal.http') }}"
|
||||
|
||||
JENKINS_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
JENKINS_MCP_USERNAME: "{{ lookup('users', 'administrator').username }}"
|
||||
JENKINS_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
JENKINS_MCP_USERNAME: "{{ lookup('config', application_id, 'mcp.credential.owner') }}"
|
||||
JENKINS_MCP_TOKEN_NAME: "infinito-mcp"
|
||||
JENKINS_MCP_TOKEN_FILE: "/var/jenkins_home/secrets/infinito-mcp.token"
|
||||
JENKINS_MCP_IDENTITY_URL: "{{ JENKINS_LOOPBACK_URL }}/whoAmI/api/json"
|
||||
JENKINS_MCP_READY_URL: "{{ JENKINS_LOOPBACK_URL }}/login"
|
||||
JENKINS_MCP_URL: "{{ JENKINS_LOOPBACK_URL }}{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}"
|
||||
JENKINS_MCP_URL: "{{ JENKINS_LOOPBACK_URL }}{{ lookup('config', application_id, 'mcp.endpoint.path') }}"
|
||||
JENKINS_MCP_INITIALIZE_PAYLOAD:
|
||||
jsonrpc: "2.0"
|
||||
id: 1
|
||||
|
||||
@@ -36,7 +36,6 @@ flowchart LR
|
||||
svc_css["css"]
|
||||
svc_prometheus["prometheus"]
|
||||
svc_container_backup["container_backup"]
|
||||
svc_mcp["mcp ❌"]
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_svc_db_postgres -. "0..1" .-> svc_postgres
|
||||
|
||||
@@ -68,11 +68,3 @@ container_backup: # nocheck: bond-baseline backup host joins outside the variant
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
|
||||
shared: true
|
||||
|
||||
# nocheck: dynamic-flag a blocker block can never be enabled, so a group_names expression would be dead code
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
blocker: hosted_only
|
||||
source_url: https://support.atlassian.com/atlassian-rovo-mcp-server/docs/getting-started-with-the-atlassian-remote-mcp-server/
|
||||
notes: Rovo MCP is Atlassian Cloud only; this role deploys an unsupported self-hosted container.
|
||||
@@ -43,6 +43,7 @@ flowchart LR
|
||||
svc_css["css"]
|
||||
svc_hcaptcha["hcaptcha"]
|
||||
svc_prometheus["prometheus"]
|
||||
svc_listmonkmcp["listmonkmcp"]
|
||||
end
|
||||
dep_svc_db_postgres -. "0..1" .-> svc_postgres
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
enabled: false
|
||||
shared: false
|
||||
classification: adapter_server
|
||||
direction: server
|
||||
source_url: https://s.infinito.nexus/code
|
||||
supported_version: 1.0.0
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: adapter
|
||||
credential:
|
||||
owner: mcp-web-app-listmonk
|
||||
source: credentials
|
||||
key: mcp_bearer
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: listmonkmcp
|
||||
path: /mcp
|
||||
port_key: http
|
||||
adapter:
|
||||
type: openapi_allowlist
|
||||
image: infinito/mcp-adapter
|
||||
version: 1.0.0
|
||||
digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
|
||||
upstream_api_version: v1
|
||||
specification_path: roles/web-app-listmonk/files/mcp/tools.json
|
||||
specification_sha256: sha256:3459dd13cfef868ed4de0123695292c8d75c8d3c612bef78fa8d5eaba297f06e
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 100
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- listmonk_get_campaign
|
||||
- listmonk_get_stats
|
||||
- listmonk_list_campaigns
|
||||
schema_sha256: sha256:3459dd13cfef868ed4de0123695292c8d75c8d3c612bef78fa8d5eaba297f06e
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -82,3 +82,18 @@ prometheus:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-prometheus' in group_names }}"
|
||||
shared: "{{ 'web-app-prometheus' in group_names }}"
|
||||
# nocheck: playwright-service-flag internal adapter sidecar, covered by the role's MCP guest spec
|
||||
# nocheck: dockerfile-custom the Dockerfile is owned by svc-ai-mcp-adapter and staged per instance, so every adapter-backed provider shares one audited build
|
||||
listmonkmcp:
|
||||
image: python
|
||||
# nocheck: docker-version base of the self-built stdlib adapter, not the served application
|
||||
version: "3.13-slim"
|
||||
custom: true
|
||||
name: listmonkmcp
|
||||
cpus: "0.25"
|
||||
mem_reservation: 16m
|
||||
mem_limit: 24m
|
||||
pids_limit: 64
|
||||
ports:
|
||||
internal:
|
||||
http: 8080
|
||||
@@ -34,6 +34,9 @@
|
||||
css:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
asset:
|
||||
enabled: false
|
||||
@@ -64,3 +67,6 @@
|
||||
css:
|
||||
enabled: false
|
||||
shared: false
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -20,11 +20,9 @@ flowchart LR
|
||||
dep_svc_db_postgres["svc-db-postgres 🐳🐝"]
|
||||
dep_svc_db_redis["svc-db-redis 🐳🐝"]
|
||||
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
|
||||
dep_web_app_hermes["web-app-hermes 🐳🐝"]
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_mailu["web-app-mailu 🐳🐝"]
|
||||
dep_web_app_matomo["web-app-matomo 🐳🐝"]
|
||||
dep_web_app_openclaw["web-app-openclaw 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
dep_web_app_seaweedfs["web-app-seaweedfs 🐳🐝"]
|
||||
dep_web_svc_css["web-svc-css 💻"]
|
||||
@@ -39,7 +37,6 @@ flowchart LR
|
||||
svc_email["email"]
|
||||
svc_postgres["postgres"]
|
||||
svc_mattermost["mattermost"]
|
||||
svc_mcp["mcp"]
|
||||
svc_redis["redis"]
|
||||
svc_minio["minio ❌"]
|
||||
svc_seaweedfs["seaweedfs"]
|
||||
@@ -56,11 +53,9 @@ flowchart LR
|
||||
dep_svc_db_postgres -. "0..1" .-> svc_postgres
|
||||
dep_svc_db_redis -. "0..1" .-> svc_redis
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_app_seaweedfs -. "0..1" .-> svc_seaweedfs
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
@@ -142,7 +137,7 @@ Mattermost exposes a Model Context Protocol server through the prepackaged Agent
|
||||
|
||||
### Default state
|
||||
|
||||
`services.mcp.enabled` resolves to `true` only when `web-app-hermes` or `web-app-openclaw` is part of the same deployment, and is `false` otherwise. While it is `true` the deploy:
|
||||
`mcp.enabled` resolves to `true` only when `web-app-hermes` or `web-app-openclaw` is part of the same deployment, and is `false` otherwise. While it is `true` the deploy:
|
||||
|
||||
- sets `MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS=true`,
|
||||
- enables the `mattermost-ai` plugin through `mmctl --local`,
|
||||
@@ -170,14 +165,14 @@ The endpoint serves the Agents plugin's native Mattermost tool catalog:
|
||||
|
||||
The catalog includes mutating entries (`create`, `update`, `archive`, `delete`,
|
||||
`send`, `upload`). The Agents plugin exposes no filter, scope or permission flag
|
||||
that removes them, so `services.mcp.tools.mutating_tools_enabled: false` records
|
||||
that removes them, so `mcp.tools.mutating_tools_enabled: false` records
|
||||
the deployment's intent rather than an enforced state. Every call is bounded by
|
||||
the permissions of the account the bearer token belongs to, which here is the
|
||||
administrator.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles, or pin `services.mcp.enabled: false` for this role. The Agents plugin's MCP server is then left switched off and no personal access token is issued.
|
||||
Remove the MCP client roles, or pin `mcp.enabled: false` for this role. The Agents plugin's MCP server is then left switched off and no personal access token is issued.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: plugin_server
|
||||
direction: server
|
||||
source_url: https://docs.mattermost.com/agents/mcpserver/README.html
|
||||
minimum_version: "10.0"
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: plugin
|
||||
credential:
|
||||
owner: mcp-web-app-mattermost
|
||||
source: token_store
|
||||
key: web-app-mattermost
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: mattermost
|
||||
path: /plugins/mattermost-ai/mcp-server/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
allowlist:
|
||||
- add_user_to_channel
|
||||
- create_channel
|
||||
- create_post
|
||||
- dm
|
||||
- get_channel_info
|
||||
- get_channel_members
|
||||
- get_team_info
|
||||
- get_team_members
|
||||
- get_user_channels
|
||||
- group_message
|
||||
- list_agents
|
||||
- read_channel
|
||||
- read_file
|
||||
- read_post
|
||||
- search_posts
|
||||
- search_users
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -58,26 +58,6 @@ mattermost:
|
||||
- web-app-keycloak
|
||||
- web-app-mailu
|
||||
lifecycle: beta
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://docs.mattermost.com/agents/mcpserver/README.html
|
||||
minimum_version: "10.0"
|
||||
notes: Ships with the Agents plugin.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: administrator
|
||||
implementation: plugin
|
||||
endpoint:
|
||||
service_key: mattermost
|
||||
path: /plugins/mattermost-ai/mcp-server/mcp
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: dynamic-flag
|
||||
# nocheck: playwright-service-flag — DB engine, covered by role-local integration tests
|
||||
redis:
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
matomo:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
sso:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -38,6 +35,9 @@
|
||||
css:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -54,9 +54,6 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
@@ -73,3 +70,6 @@
|
||||
css:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -97,5 +97,5 @@
|
||||
- (mattermost_mcp_toggle_final.rows[0][0] | trim) == 'true'
|
||||
fail_msg: >-
|
||||
The Agents plugin still reports mcp.enablePluginServer=false, so
|
||||
{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}
|
||||
{{ lookup('config', application_id, 'mcp.endpoint.path') }}
|
||||
answers 404 and every MCP client would be pointed at a dead endpoint.
|
||||
@@ -24,7 +24,7 @@ MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enab
|
||||
|
||||
MATTERMOST_BASE_URL={{ lookup('tls', 'web-app-mattermost', 'url.base') | dotenv_quote }}
|
||||
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
OIDC_ISSUER_URL={{ OIDC.CLIENT.ISSUER_URL | dotenv_quote }}
|
||||
|
||||
|
||||
@@ -7,10 +7,9 @@ container_port: "{{ lookup('config', application_id, 'services.' ~ entity_name ~
|
||||
MATTERMOST_CONTAINER: "{{ lookup('config', application_id, 'services.mattermost.name') }}"
|
||||
MATTERMOST_CONTAINER_ADDRESS: "{{ lookup('container_address', application_id, entity_name) }}"
|
||||
|
||||
# Exception: OIDC piggybacks on MM_GITLABSETTINGS_* -- Team Edition has no native OIDC provider for Keycloak
|
||||
MATTERMOST_OIDC_ENABLED: "{{ lookup('config', application_id, 'services.sso.enabled') }}"
|
||||
|
||||
MATTERMOST_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
MATTERMOST_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
MATTERMOST_MCP_PLUGIN_ID: mattermost-ai
|
||||
MATTERMOST_MCP_TOKEN_DESCRIPTION: mcp
|
||||
|
||||
|
||||
@@ -19,12 +19,9 @@ flowchart LR
|
||||
dep_svc_db_mariadb["svc-db-mariadb 🐳🐝"]
|
||||
dep_svc_db_openldap["svc-db-openldap 🐳🐝"]
|
||||
dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
|
||||
dep_web_app_hermes["web-app-hermes 🐳🐝"]
|
||||
dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
|
||||
dep_web_app_mailu["web-app-mailu 🐳🐝"]
|
||||
dep_web_app_matomo["web-app-matomo 🐳🐝"]
|
||||
dep_web_app_openclaw["web-app-openclaw 🐳🐝"]
|
||||
dep_web_app_openwebui["web-app-openwebui 🐳🐝"]
|
||||
dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
|
||||
dep_web_svc_css["web-svc-css 💻"]
|
||||
dep_web_svc_logout["web-svc-logout 🐳🐝"]
|
||||
@@ -36,7 +33,6 @@ flowchart LR
|
||||
svc_matomo["matomo"]
|
||||
svc_email["email"]
|
||||
svc_ldap["ldap"]
|
||||
svc_mcp["mcp"]
|
||||
svc_mariadb["mariadb"]
|
||||
svc_moodle["moodle"]
|
||||
svc_nginx["nginx"]
|
||||
@@ -49,12 +45,9 @@ flowchart LR
|
||||
dep_svc_db_mariadb -. "0..1" .-> svc_mariadb
|
||||
dep_svc_db_openldap -. "0..1" .-> svc_ldap
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_openwebui -. "0..1" .-> svc_mcp
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
dep_web_svc_logout -. "0..1" .-> svc_logout
|
||||
@@ -120,7 +113,7 @@ The role exposes a Model Context Protocol surface through the `webservice_mcp` p
|
||||
| Transport | Streamable HTTP (JSON-RPC 2.0 over `POST`) |
|
||||
| Auth | `Authorization: Bearer <token>`, a Moodle web-service token |
|
||||
| Subject | the `administrator` account |
|
||||
| Default state | off; `services.mcp.enabled` turns true when `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is in the inventory |
|
||||
| Default state | off; `mcp.enabled` turns true when `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is in the inventory |
|
||||
|
||||
Provisioning runs in `tasks/06_mcp.yml`: it copies the plugin into the code volume, runs `admin/cli/upgrade.php`, enables `enablewebservices`, appends `mcp` to `webserviceprotocols`, creates the `infinito_mcp` external service, attaches the read-only functions, mints the permanent token and stores it under `users.administrator.tokens['web-app-moodle']`.
|
||||
|
||||
@@ -131,11 +124,11 @@ Tool categories exposed by the external service:
|
||||
- user lookup (`core_user_get_users_by_field`)
|
||||
- calendar events (`core_calendar_get_calendar_events`)
|
||||
|
||||
All of them are read-only; `services.mcp.tools.mutating_tools_enabled` is `false` and no write function is attached.
|
||||
All of them are read-only; `mcp.tools.mutating_tools_enabled` is `false` and no write function is attached.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is true only while `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is part of the deployment.
|
||||
Off. `mcp.enabled` is true only while `web-app-hermes`, `web-app-openclaw` or `web-app-openwebui` is part of the deployment.
|
||||
|
||||
### Authorization subject
|
||||
|
||||
@@ -151,7 +144,7 @@ Moodle compares every request against `$CFG->wwwroot`. A probe that reaches the
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles, or pin `services.mcp.enabled: false` for this role. The web-service token is then not issued and the protocol plugin stays unconfigured.
|
||||
Remove the MCP client roles, or pin `mcp.enabled: false` for this role. The web-service token is then not issued and the protocol plugin stays unconfigured.
|
||||
|
||||
## Image source
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: plugin_server
|
||||
direction: server
|
||||
source_url: https://moodle.org/plugins/webservice_mcp
|
||||
minimum_version: "4.3"
|
||||
transport: streamable_http
|
||||
exposure: public
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: plugin
|
||||
credential:
|
||||
owner: mcp-web-app-moodle
|
||||
source: token_store
|
||||
key: web-app-moodle
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: nginx
|
||||
path: /webservice/mcp/server.php
|
||||
port_key: http
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 500
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
allowlist:
|
||||
- core_calendar_get_calendar_events
|
||||
- core_course_get_courses
|
||||
- core_enrol_get_users_courses
|
||||
- core_user_get_users_by_field
|
||||
- core_webservice_get_site_info
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -25,26 +25,6 @@ ldap:
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-db-openldap' in group_names }}"
|
||||
shared: "{{ 'svc-db-openldap' in group_names }}"
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names or 'web-app-openwebui' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names or 'web-app-openwebui' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://moodle.org/plugins/webservice_mcp
|
||||
minimum_version: "4.3"
|
||||
notes: Plugin based; must match the pinned Moodle release.
|
||||
transport: streamable_http
|
||||
exposure: public
|
||||
auth: bearer_token
|
||||
auth_subject: administrator
|
||||
implementation: plugin
|
||||
endpoint:
|
||||
service_key: nginx
|
||||
path: /webservice/mcp/server.php
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: playwright-service-flag — DB engine, covered by role-local integration tests
|
||||
mariadb:
|
||||
bond: 1
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
matomo:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
sso:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -40,6 +37,9 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -68,12 +68,12 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
prometheus:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -98,12 +98,12 @@
|
||||
matomo:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
sso:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
prometheus:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -25,5 +25,5 @@ MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enab
|
||||
EMAIL_SERVICE_ENABLED={{ lookup('config', application_id, 'services.email.enabled') | string | lower }}
|
||||
LDAP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.ldap.enabled') | string | lower }}
|
||||
LAM_SERVICE_ENABLED={{ lookup('config', application_id, 'services.lam.enabled', false) | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'services.mcp.endpoint.path') | dotenv_quote }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'mcp.endpoint.path') | dotenv_quote }}
|
||||
@@ -51,15 +51,15 @@ MOODLE_CRON_CONTAINER: "{{ lookup('config', application_id, 'services.cron.name'
|
||||
MOODLE_OIDC_ENABLED: "{{ lookup('config', application_id, 'services.sso.enabled') }}"
|
||||
MOODLE_LDAP_ENABLED: "{{ lookup('config', application_id, 'services.ldap.enabled') }}"
|
||||
|
||||
MOODLE_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
MOODLE_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
MOODLE_MCP_PLUGIN_VERSION: "v0.6.0"
|
||||
MOODLE_MCP_PLUGIN_ARCHIVE_URL: "https://github.com/onbirdev/moodle-webservice_mcp/archive/refs/tags/{{ MOODLE_MCP_PLUGIN_VERSION }}.zip"
|
||||
MOODLE_MCP_PLUGIN_RELPATH: "{{ [MOODLE_WEBSERVICE_SUBDIR, 'mcp'] | path_join }}"
|
||||
MOODLE_MCP_PLUGIN_SOURCE_DIR: "{{ [MOODLE_SOURCE_DIR, MOODLE_MCP_PLUGIN_RELPATH] | path_join }}"
|
||||
MOODLE_MCP_PLUGIN_DIR: "{{ [MOODLE_CODE_DIR, MOODLE_MCP_PLUGIN_RELPATH] | path_join }}"
|
||||
MOODLE_MCP_SERVICE_KEY: "{{ lookup('config', application_id, 'services.mcp.endpoint.service_key') }}"
|
||||
MOODLE_MCP_SERVICE_KEY: "{{ lookup('config', application_id, 'mcp.endpoint.service_key') }}"
|
||||
MOODLE_MCP_PORT: "{{ lookup('config', application_id, 'services.nginx.ports.internal.http') }}"
|
||||
MOODLE_MCP_URL: "http://{{ MOODLE_MCP_SERVICE_KEY }}:{{ MOODLE_MCP_PORT }}{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}"
|
||||
MOODLE_MCP_URL: "http://{{ MOODLE_MCP_SERVICE_KEY }}:{{ MOODLE_MCP_PORT }}{{ lookup('config', application_id, 'mcp.endpoint.path') }}"
|
||||
MOODLE_MCP_EXTERNAL_SERVICE_NAME: "Model Context Protocol"
|
||||
MOODLE_MCP_EXTERNAL_SERVICE_SHORTNAME: "infinito_mcp"
|
||||
MOODLE_MCP_EXTERNAL_FUNCTIONS:
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
enabled: false
|
||||
shared: false
|
||||
classification: native_both
|
||||
direction: both
|
||||
source_url: https://github.com/n8n-io/n8n/tree/n8n%401.95.3/packages/%40n8n/nodes-langchain/nodes/mcp
|
||||
supported_version: 1.95.3
|
||||
minimum_version: 1.88.0
|
||||
transport: sse
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: service_account
|
||||
implementation: native
|
||||
credential:
|
||||
owner: mcp-web-app-n8n
|
||||
source: token_store
|
||||
key: web-app-n8n
|
||||
allowed_consumers:
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
- web-app-openwebui
|
||||
supported_transports:
|
||||
- sse
|
||||
supported_auths:
|
||||
- bearer_token
|
||||
- app_password
|
||||
- upstream_session
|
||||
endpoint:
|
||||
service_key: n8n
|
||||
path: /webhook/infinito-mcp/sse
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -26,6 +26,9 @@
|
||||
enabled: true
|
||||
shared: true
|
||||
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -53,3 +56,6 @@
|
||||
prometheus:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -57,7 +57,6 @@ flowchart LR
|
||||
svc_mariadb["mariadb"]
|
||||
svc_nextcloud["nextcloud"]
|
||||
svc_proxy["proxy"]
|
||||
svc_mcp["mcp"]
|
||||
svc_context_agent["context_agent"]
|
||||
svc_cron["cron"]
|
||||
svc_talk["talk"]
|
||||
@@ -96,7 +95,6 @@ flowchart LR
|
||||
dep_web_app_discourse -. "0..1" .-> svc_discourse
|
||||
dep_web_app_gitlab -. "0..1" .-> svc_gitlab
|
||||
dep_web_app_hermes -. "0..1" .-> svc_context_agent
|
||||
dep_web_app_hermes -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_mailu -. "0..1" .-> svc_email
|
||||
dep_web_app_mastodon -. "0..1" .-> svc_mastodon
|
||||
@@ -104,7 +102,6 @@ flowchart LR
|
||||
dep_web_app_matrix -. "0..1" .-> svc_matrix
|
||||
dep_web_app_mattermost -. "0..1" .-> svc_mattermost
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_context_agent
|
||||
dep_web_app_openclaw -. "0..1" .-> svc_mcp
|
||||
dep_web_app_openproject -. "0..1" .-> svc_openproject
|
||||
dep_web_app_openwebui -. "0..1" .-> svc_openwebui
|
||||
dep_web_app_peertube -. "0..1" .-> svc_peertube
|
||||
@@ -201,11 +198,11 @@ Nextcloud serves a Model Context Protocol endpoint through the AppAPI proxy of t
|
||||
| Auth | Nextcloud app password, sent as `Authorization: Bearer <app-password>` |
|
||||
| Identity | the Nextcloud user the app password belongs to; every tool call runs with that user's permissions |
|
||||
| Implementation | plugin (the `context_agent` ExApp container, registered through the `manual_install` deploy daemon) |
|
||||
| Default state | off; `services.mcp.enabled` turns on when `web-app-hermes` or `web-app-openclaw` is deployed |
|
||||
| Default state | off; `mcp.enabled` turns on when `web-app-hermes` or `web-app-openclaw` is deployed |
|
||||
|
||||
### Deployment
|
||||
|
||||
The `context_agent` service in [`meta/services.yml`](./meta/services.yml) renders only while `services.mcp.enabled` is true. It runs `ghcr.io/nextcloud/context_agent`, listens on its internal port for AppAPI only, and shares `credentials.context_agent_app_secret` with the ExApp registration as `APP_SECRET`.
|
||||
The `context_agent` service in [`meta/services.yml`](./meta/services.yml) renders only while `mcp.enabled` is true. It runs `ghcr.io/nextcloud/context_agent`, listens on its internal port for AppAPI only, and shares `credentials.context_agent_app_secret` with the ExApp registration as `APP_SECRET`.
|
||||
|
||||
[`tasks/03_mcp.yml`](./tasks/03_mcp.yml) waits for the ExApp heartbeat, registers the deploy daemon and the ExApp (route `^/mcp`, verbs `POST,GET,DELETE`, access level `1`), enables the ExApp, mints an app password via `occ user:auth-tokens:add`, persists it through `sys-token-store` under `users.administrator.tokens['web-app-nextcloud']`, and asserts that an authenticated `initialize` call answers `200`.
|
||||
|
||||
@@ -215,7 +212,7 @@ The `context_agent` service in [`meta/services.yml`](./meta/services.yml) render
|
||||
|
||||
### Tool categories
|
||||
|
||||
The ExApp registers every tool category the Context Agent ships, read-only and mutating alike, and each call runs with the permissions of the app-password owner. The 2.7.0 image advertises 94 tools across 23 categories, which are application names (`calendar`, `files`, `mail`, `talk`, …), not operations: each bundles its read and its write tools. `tool_status` therefore switches whole categories, and no setting removes only the mutating ones, so `services.mcp.tools.mutating_tools_enabled: false` records the deployment's intent rather than an enforced state. Disabling a category to bar its write tools also removes its read tools. List the categories and narrow the exposed set with:
|
||||
The ExApp registers every tool category the Context Agent ships, read-only and mutating alike, and each call runs with the permissions of the app-password owner. The 2.7.0 image advertises 94 tools across 23 categories, which are application names (`calendar`, `files`, `mail`, `talk`, …), not operations: each bundles its read and its write tools. `tool_status` therefore switches whole categories, and no setting removes only the mutating ones, so `mcp.tools.mutating_tools_enabled: false` records the deployment's intent rather than an enforced state. Disabling a category to bar its write tools also removes its read tools. List the categories and narrow the exposed set with:
|
||||
|
||||
```bash
|
||||
occ app_api:app:config:get context_agent tool_status
|
||||
@@ -237,11 +234,11 @@ An unauthenticated request to the same path answers `404`; [`files/playwright/te
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is true only while `web-app-hermes` or `web-app-openclaw` is part of the deployment.
|
||||
Off. `mcp.enabled` is true only while `web-app-hermes` or `web-app-openclaw` is part of the deployment.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP client roles, or pin `services.mcp.enabled: false` for this role. The `context_agent` service is then not rendered, the ExApp is not registered with AppAPI, and no app password is minted.
|
||||
Remove the MCP client roles, or pin `mcp.enabled: false` for this role. The `context_agent` service is then not rendered, the ExApp is not registered with AppAPI, and no app password is minted.
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: plugin_server
|
||||
direction: server
|
||||
source_url: https://docs.nextcloud.com/server/latest/admin_manual/ai/app_context_agent.html
|
||||
minimum_version: "30"
|
||||
transport: streamable_http
|
||||
exposure: public
|
||||
auth: app_password
|
||||
auth_subject: service_account
|
||||
implementation: plugin
|
||||
credential:
|
||||
owner: mcp-web-app-nextcloud
|
||||
source: token_store
|
||||
key: web-app-nextcloud
|
||||
allowed_consumers:
|
||||
- web-app-openwebui
|
||||
- web-app-hermes
|
||||
- web-app-openclaw
|
||||
endpoint:
|
||||
service_key: proxy
|
||||
path: /index.php/apps/app_api/proxy/context_agent/mcp/
|
||||
port_key: http
|
||||
limits:
|
||||
request_bytes: 65536
|
||||
response_bytes: 1048576
|
||||
timeout_seconds: 15
|
||||
concurrent_requests: 4
|
||||
page_size: 100
|
||||
result_items: 500
|
||||
stream_seconds: 300
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -115,26 +115,6 @@ proxy:
|
||||
ports:
|
||||
internal:
|
||||
http: 80
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
shared: "{{ 'web-app-hermes' in group_names or 'web-app-openclaw' in group_names }}"
|
||||
direction: server
|
||||
source_url: https://docs.nextcloud.com/server/latest/admin_manual/ai/app_context_agent.html
|
||||
minimum_version: "30"
|
||||
notes: Context Agent ExApp behind the AppAPI proxy, app-password auth.
|
||||
transport: streamable_http
|
||||
exposure: public
|
||||
auth: app_password
|
||||
auth_subject: administrator
|
||||
implementation: plugin
|
||||
endpoint:
|
||||
service_key: proxy
|
||||
path: /index.php/apps/app_api/proxy/context_agent/mcp/
|
||||
port_key: http
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
# nocheck: playwright-service-flag
|
||||
context_agent:
|
||||
bond: 1
|
||||
|
||||
@@ -30,9 +30,6 @@
|
||||
dashboard:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
context_agent:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -100,6 +97,9 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -119,9 +119,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -191,6 +188,9 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -210,9 +210,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -282,6 +279,9 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -301,9 +301,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -373,6 +370,9 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -392,9 +392,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -464,6 +461,9 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -483,9 +483,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -555,6 +552,9 @@
|
||||
peertube:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -574,9 +574,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -646,6 +643,9 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -665,9 +665,6 @@
|
||||
dashboard:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
context_agent:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -737,3 +734,6 @@
|
||||
peertube:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -25,7 +25,7 @@ MATOMO_BASE_URL={{ lookup('tls', 'web-app-matomo', 'url.base') | dotenv_quote }}
|
||||
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
MOODLE_BASE_URL={{ (lookup('tls', 'web-app-moodle', 'url.base') if (lookup('config', application_id, 'services.moodle.enabled') | bool) else '') | dotenv_quote }}
|
||||
|
||||
@@ -33,7 +33,7 @@ NEXTCLOUD_BASE_URL={{ lookup('tls', 'web-app-nextcloud', 'url.base') | dotenv_qu
|
||||
|
||||
NEXTCLOUD_DIRECT_LOGIN_PASSWORD={{ lookup('config', application_id, 'credentials.administrator_password') | dotenv_quote }}
|
||||
|
||||
NEXTCLOUD_MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'services.mcp.endpoint.path') | dotenv_quote }}
|
||||
NEXTCLOUD_MCP_ENDPOINT_PATH={{ lookup('config', application_id, 'mcp.endpoint.path') | dotenv_quote }}
|
||||
|
||||
NEXTCLOUD_ONLYOFFICE_EXPECTED_DOCUMENT_SERVER_URL={{ NEXTCLOUD_ONLYOFFICE_URL | dotenv_quote }}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ NEXTCLOUD_PROXY_SERVICE: "proxy"
|
||||
NEXTCLOUD_PROXY_CONTAINER: "{{ lookup('config', application_id, 'services.' ~ NEXTCLOUD_PROXY_SERVICE ~ '.name') }}"
|
||||
NEXTCLOUD_PROXY_DNS_NAME: "{{ (entity_name ~ '_' ~ NEXTCLOUD_PROXY_SERVICE) if DEPLOYMENT_MODE == 'swarm' else NEXTCLOUD_PROXY_CONTAINER }}"
|
||||
|
||||
NEXTCLOUD_MCP_ENABLED: "{{ lookup('config', application_id, 'services.mcp.enabled') }}"
|
||||
NEXTCLOUD_MCP_PATH: "{{ lookup('config', application_id, 'services.mcp.endpoint.path') }}"
|
||||
NEXTCLOUD_MCP_ENABLED: "{{ lookup('config', application_id, 'mcp.enabled') }}"
|
||||
NEXTCLOUD_MCP_PATH: "{{ lookup('config', application_id, 'mcp.endpoint.path') }}"
|
||||
NEXTCLOUD_MCP_TOKEN_NAME: "infinito-mcp"
|
||||
|
||||
NEXTCLOUD_CONTEXT_AGENT_SERVICE: "context_agent"
|
||||
|
||||
@@ -41,7 +41,6 @@ flowchart LR
|
||||
svc_javascript["javascript"]
|
||||
svc_prometheus["prometheus"]
|
||||
svc_container_backup["container_backup"]
|
||||
svc_mcp["mcp ❌"]
|
||||
end
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_svc_db_openldap -. "0..1" .-> svc_ldap
|
||||
|
||||
@@ -96,12 +96,3 @@ container_backup: # nocheck: bond-baseline backup host joins outside the variant
|
||||
bond: 1
|
||||
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
|
||||
shared: true
|
||||
|
||||
# nocheck: dynamic-flag a blocker block can never be enabled, so a group_names expression would be dead code
|
||||
# nocheck: playwright-service-flag no MCP surface is served, so no spec can exercise one
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
blocker: hosted_only
|
||||
source_url: https://apps.odoo.com/apps
|
||||
notes: Third-party add-ons only; none has been selected and reviewed.
|
||||
@@ -30,7 +30,6 @@ flowchart LR
|
||||
svc_kata["kata"]
|
||||
svc_sso["sso"]
|
||||
svc_litellm["litellm"]
|
||||
svc_mcp["mcp"]
|
||||
svc_logout["logout"]
|
||||
svc_dashboard["dashboard"]
|
||||
svc_matomo["matomo"]
|
||||
@@ -41,31 +40,18 @@ flowchart LR
|
||||
end
|
||||
subgraph dependents [Dependents]
|
||||
dpt_svc_ai_robot["svc-ai-robot 💻"]
|
||||
dpt_web_app_gitea["web-app-gitea 🐳🐝"]
|
||||
dpt_web_app_gitlab["web-app-gitlab 🐳🐝"]
|
||||
dpt_web_app_homeassistant["web-app-homeassistant 🐳🐝"]
|
||||
dpt_web_app_jenkins["web-app-jenkins 🐳🐝"]
|
||||
dpt_web_app_mattermost["web-app-mattermost 🐳🐝"]
|
||||
dpt_web_app_moodle["web-app-moodle 🐳🐝"]
|
||||
dpt_web_app_nextcloud["web-app-nextcloud 🐳🐝"]
|
||||
end
|
||||
dep_svc_ai_litellm -. "0..1" .-> svc_litellm
|
||||
dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
|
||||
dep_web_app_dashboard -. "0..1" .-> svc_dashboard
|
||||
dep_web_app_homeassistant -. "0..1" .-> svc_homeassistant
|
||||
dep_web_app_homeassistant -. "0..1" .-> svc_mcp
|
||||
dep_web_app_keycloak -. "0..1" .-> svc_sso
|
||||
dep_web_app_matomo -. "0..1" .-> svc_matomo
|
||||
dep_web_app_prometheus -. "0..1" .-> svc_prometheus
|
||||
dep_web_svc_css -. "0..1" .-> svc_css
|
||||
dep_web_svc_logout -. "0..1" .-> svc_logout
|
||||
svc_openclaw -. "0..1" .-> dpt_svc_ai_robot
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_gitea
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_gitlab
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_homeassistant
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_jenkins
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_mattermost
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_moodle
|
||||
svc_openclaw -. "0..1" .-> dpt_web_app_nextcloud
|
||||
```
|
||||
|
||||
@@ -148,12 +134,12 @@ gateway holding the MCP credentials refuses an unauthenticated caller.
|
||||
|
||||
### Default state
|
||||
|
||||
Off. `services.mcp.enabled` is false unless an MCP server role is part of the
|
||||
Off. `mcp.enabled` is false unless an MCP server role is part of the
|
||||
deployment.
|
||||
|
||||
### How to disable
|
||||
|
||||
Remove the MCP server roles, or pin `services.mcp.enabled: false` for this role.
|
||||
Remove the MCP server roles, or pin `mcp.enabled: false` for this role.
|
||||
The rendered config then contains no MCP server entry.
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
enabled: true
|
||||
shared: true
|
||||
classification: native_client
|
||||
direction: client
|
||||
source_url: https://openclaw.ai/
|
||||
supported_version: "2026.7.1"
|
||||
minimum_version: "2025.1"
|
||||
implementation: native
|
||||
supported_transports:
|
||||
- streamable_http
|
||||
- sse
|
||||
supported_auths:
|
||||
- bearer_token
|
||||
- app_password
|
||||
- upstream_session
|
||||
- basic_auth
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
@@ -55,24 +55,6 @@ litellm:
|
||||
enabled: "{{ 'svc-ai-litellm' in group_names }}"
|
||||
shared: "{{ 'svc-ai-litellm' in group_names }}"
|
||||
|
||||
# nocheck: shared-provider
|
||||
mcp:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-app-homeassistant' in group_names }}"
|
||||
shared: "{{ 'web-app-homeassistant' in group_names }}"
|
||||
direction: client
|
||||
source_url: https://openclaw.ai/
|
||||
minimum_version: "2025.1"
|
||||
notes: Native @modelcontextprotocol/sdk, client and server.
|
||||
transport: streamable_http
|
||||
exposure: internal
|
||||
auth: bearer_token
|
||||
auth_subject: user
|
||||
implementation: native
|
||||
tools:
|
||||
read_only_default: true
|
||||
mutating_tools_enabled: false
|
||||
|
||||
logout:
|
||||
bond: 1
|
||||
enabled: "{{ 'web-svc-logout' in group_names }}"
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
litellm:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
logout:
|
||||
enabled: true
|
||||
shared: true
|
||||
@@ -32,6 +29,9 @@
|
||||
homeassistant:
|
||||
enabled: true
|
||||
shared: true
|
||||
mcp:
|
||||
enabled: true
|
||||
shared: true
|
||||
- services:
|
||||
container_backup:
|
||||
enabled: false
|
||||
@@ -42,9 +42,6 @@
|
||||
litellm:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
logout:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
@@ -63,3 +60,6 @@
|
||||
homeassistant:
|
||||
enabled: false
|
||||
shared: false # nocheck: variants-static-override backup shared stays literally true
|
||||
mcp:
|
||||
enabled: false
|
||||
shared: false
|
||||
@@ -22,7 +22,7 @@ LOGOUT_SERVICE_ENABLED={{ lookup('config', application_id, 'services.logout.enab
|
||||
MATOMO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.matomo.enabled') | string | lower }}
|
||||
|
||||
# nocheck: playwright-service-gate
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'services.mcp.enabled') | string | lower }}
|
||||
MCP_SERVICE_ENABLED={{ lookup('config', application_id, 'mcp.enabled') | string | lower }}
|
||||
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ OPENCLAW_CONTAINER: "{{ lookup('config', application_id, 'services.openclaw.name
|
||||
OPENCLAW_PORT_INTERNAL: "{{ lookup('config', application_id, 'services.openclaw.ports.internal.http') }}"
|
||||
OPENCLAW_URL: "{{ lookup('tls', application_id, 'url.base') }}"
|
||||
OPENCLAW_CONFIG_PATH_HOST: "{{ [ lookup('container', application_id, 'directories.config'), 'openclaw.json' ] | path_join }}"
|
||||
OPENCLAW_MCP_SERVERS: "{{ MCP_DISCOVERED_SERVERS if (lookup('config', application_id, 'services.mcp.enabled') | bool) else [] }}"
|
||||
OPENCLAW_MCP_SERVERS: "{{ MCP_DISCOVERED_SERVERS if (lookup('config', application_id, 'mcp.enabled') | bool) else [] }}"
|
||||
Loaded 100 of 143 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user