Skip to content

Commit 5446928

Browse files
committed
metamcp: add reconciliation knobs (runOnUpgrade, updateExisting); docs: README sections + examples; bump to 0.1.22
1 parent 2a0e5d9 commit 5446928

File tree

7 files changed

+98
-11
lines changed

7 files changed

+98
-11
lines changed

charts/metamcp/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: v2
22
name: metamcp
33
description: MetaMCP aggregator Helm chart for Kubernetes
44
type: application
5-
version: 0.1.21
5+
version: 0.1.22
66
appVersion: "latest"
77
icon: https://icoretech.github.io/helm/charts/metamcp/logo.png
88
keywords:

charts/metamcp/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,42 @@ users:
112112
apiKeyName: cli
113113
```
114114

115+
### Reconciliation modes (Flux/Helm upgrades)
116+
117+
By default the provisioning job runs after every Helm install and upgrade and will upsert Namespaces/Endpoints to match values. You can tune this behavior:
118+
119+
- `provision.runOnUpgrade` (bool, default `true`)
120+
- `true`: run provisioning on every Helm upgrade (continuous upsert)
121+
- `false`: run only on initial install (post-install). Upgrades do not re-run provisioning
122+
123+
- `provision.updateExisting` (bool, default `true`)
124+
- `true`: update existing Namespaces (description + membership) and Endpoints when names already exist
125+
- `false`: create-only; existing objects are left untouched (good for “seed once, then manage via UI”)
126+
127+
Notes
128+
- Servers are always create-only (by name). The job does not modify or delete existing servers.
129+
- Deletions are not automatic. Removing items from values does not delete them in MetaMCP. If you need prune semantics, open an issue so we can add an opt‑in `prune` mode.
130+
131+
Examples
132+
133+
Continuous upsert (Git is the source of truth):
134+
135+
```yaml
136+
provision:
137+
enabled: true
138+
runOnUpgrade: true
139+
updateExisting: true
140+
```
141+
142+
Seed once, keep UI changes later:
143+
144+
```yaml
145+
provision:
146+
enabled: true
147+
runOnUpgrade: false
148+
updateExisting: false
149+
```
150+
115151
Provisioning authentication
116152

117153
- The provisioning Job authenticates using the first entry in `users` (email/password).

charts/metamcp/README.md.gotmpl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,42 @@ users:
112112
apiKeyName: cli
113113
```
114114

115+
### Reconciliation modes (Flux/Helm upgrades)
116+
117+
By default the provisioning job runs after every Helm install and upgrade and will upsert Namespaces/Endpoints to match values. You can tune this behavior:
118+
119+
- `provision.runOnUpgrade` (bool, default `true`)
120+
- `true`: run provisioning on every Helm upgrade (continuous upsert)
121+
- `false`: run only on initial install (post-install). Upgrades do not re-run provisioning
122+
123+
- `provision.updateExisting` (bool, default `true`)
124+
- `true`: update existing Namespaces (description + membership) and Endpoints when names already exist
125+
- `false`: create-only; existing objects are left untouched (good for “seed once, then manage via UI”)
126+
127+
Notes
128+
- Servers are always create-only (by name). The job does not modify or delete existing servers.
129+
- Deletions are not automatic. Removing items from values does not delete them in MetaMCP. If you need prune semantics, open an issue so we can add an opt‑in `prune` mode.
130+
131+
Examples
132+
133+
Continuous upsert (Git is the source of truth):
134+
135+
```yaml
136+
provision:
137+
enabled: true
138+
runOnUpgrade: true
139+
updateExisting: true
140+
```
141+
142+
Seed once, keep UI changes later:
143+
144+
```yaml
145+
provision:
146+
enabled: true
147+
runOnUpgrade: false
148+
updateExisting: false
149+
```
150+
115151
Provisioning authentication
116152

117153
- The provisioning Job authenticates using the first entry in `users` (email/password).

charts/metamcp/scripts/provision.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def log(msg):
1515
BACKEND = f"http://{SVC}:12009"
1616
ADMIN_EMAIL = os.environ.get('ADMIN_EMAIL','[email protected]')
1717
ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD','change-me')
18+
UPDATE_EXISTING = (os.environ.get('UPDATE_EXISTING','true').lower() == 'true')
1819

1920
cfg = json.load(open('/cfg/provision.json','r')).get('provision',{})
2021
servers = cfg.get('servers', [])
@@ -260,11 +261,12 @@ def k8s_get_configmap_data(name: str):
260261

261262
def ensure_namespace(name, description=None):
262263
ns_uuid = None
264+
existed = False
263265
lr = trpc_get('/trpc/frontend/frontend.namespaces.list?input=%7B%7D')
264266
if lr.ok:
265267
for ns in lr.json().get('result',{}).get('data',{}).get('data',[]):
266268
if ns.get('name') == name:
267-
ns_uuid = ns.get('uuid'); break
269+
ns_uuid = ns.get('uuid'); existed = True; break
268270
if not ns_uuid:
269271
payload = {'name': name}
270272
if description:
@@ -277,26 +279,27 @@ def ensure_namespace(name, description=None):
277279
except Exception:
278280
pass
279281
else:
280-
# Update description if provided
281-
if description:
282+
# Update description if provided and allowed
283+
if description and UPDATE_EXISTING:
282284
try:
283285
trpc_post('/trpc/frontend/frontend.namespaces.update', {'uuid': ns_uuid,'name': name,'description': description})
284286
except Exception:
285287
pass
286-
return ns_uuid
288+
return ns_uuid, existed
287289

288290
for ns in namespaces:
289291
name = ns.get('name'); nssrvs = ns.get('servers') or []
290292
if not name: continue
291293
desc = ns.get('description')
292-
nid = ensure_namespace(name, desc)
294+
nid, existed = ensure_namespace(name, desc)
293295
# Map server names to UUIDs if available
294296
srv_ids = []
295297
for nm in nssrvs:
296298
sid = srv_map.get(nm)
297299
if sid:
298300
srv_ids.append(sid)
299-
if nid and srv_ids:
301+
# Update namespace membership if allowed or if the namespace was just created
302+
if nid and srv_ids and (UPDATE_EXISTING or not existed):
300303
try:
301304
payload = {'uuid': nid, 'name': name, 'mcpServerUuids': srv_ids}
302305
if desc:
@@ -305,7 +308,7 @@ def ensure_namespace(name, description=None):
305308
except Exception:
306309
pass
307310

308-
def create_endpoint(name, nsref, transport='SSE', extra=None, description=None):
311+
def create_endpoint(name, nsref, transport='SSE', extra=None, description=None, update_existing=True):
309312
lr = trpc_get('/trpc/frontend/frontend.namespaces.list?input=%7B%7D')
310313
nid = None
311314
if lr.ok:
@@ -335,7 +338,7 @@ def create_endpoint(name, nsref, transport='SSE', extra=None, description=None):
335338
flags[k] = extra[k]
336339
if description:
337340
flags['description'] = description
338-
if e_uuid:
341+
if e_uuid and update_existing:
339342
# update existing endpoint (use tRPC batch form as UI does)
340343
up = {'uuid': e_uuid, 'name': name, 'namespaceUuid': nid}
341344
up.update(flags)
@@ -353,7 +356,7 @@ def create_endpoint(name, nsref, transport='SSE', extra=None, description=None):
353356
name = ep.get('name'); nsref = ep.get('namespace') or ep.get('namespaceUuid')
354357
if not (name and nsref): continue
355358
extra = {k: ep[k] for k in ('enableApiKeyAuth','enableOauth','useQueryParamAuth') if k in ep}
356-
create_endpoint(name, nsref, ep.get('transport'), extra, ep.get('description'))
359+
create_endpoint(name, nsref, ep.get('transport'), extra, ep.get('description'), UPDATE_EXISTING)
357360

358361
# Post-fix auto-generated endpoint servers URLs when APP_URL pointed to 12008 at creation time.
359362
# Newer MetaMCP creates a server named '<namespace>-endpoint' per endpoint and derives its URL from APP_URL.

charts/metamcp/templates/provision-config.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ metadata:
5050
{{- include "metamcp.labels" . | nindent 4 }}
5151
app.kubernetes.io/component: provision
5252
annotations:
53-
helm.sh/hook: post-install,post-upgrade
53+
helm.sh/hook: post-install{{- if (default true .Values.provision.runOnUpgrade) }},post-upgrade{{- end }}
5454
helm.sh/hook-weight: "5"
5555
helm.sh/hook-delete-policy: before-hook-creation
5656
spec:
@@ -74,6 +74,8 @@ spec:
7474
valueFrom:
7575
fieldRef:
7676
fieldPath: metadata.namespace
77+
- name: UPDATE_EXISTING
78+
value: {{ (default true .Values.provision.updateExisting) | ternary "true" "false" | quote }}
7779
- name: SVC
7880
value: {{ printf "%s.%s.svc.cluster.local" (include "metamcp.fullname" .) .Release.Namespace | quote }}
7981
- name: FRONTEND_PORT

charts/metamcp/values.schema.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"type": "object",
3131
"properties": {
3232
"enabled": { "type": "boolean" },
33+
"runOnUpgrade": { "type": "boolean", "default": true },
34+
"updateExisting": { "type": "boolean", "default": true },
3335
"servers": {
3436
"type": "array",
3537
"items": {

charts/metamcp/values.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ auth:
106106
# Generalized provisioning (multiple servers, namespaces, endpoints)
107107
provision:
108108
enabled: false
109+
# Control when the provisioning job runs
110+
# - runOnUpgrade: if true, run on every Helm upgrade (default)
111+
# - if false, run only on initial install (post-install hook only)
112+
runOnUpgrade: true
113+
# Control whether existing namespaces/endpoints are updated when they already exist.
114+
# When false, the job will only create missing objects and leave existing ones untouched
115+
# (useful for "initial provision then manage via UI").
116+
updateExisting: true
109117
# Single source of truth for servers MetaMCP exposes. Each entry can be:
110118
# - STDIO (MetaMCP runs it)
111119
# - STREAMABLE_HTTP/SSE with either:

0 commit comments

Comments
 (0)