mcp-keycloak-admin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3519 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ // src/config/config.ts
7
+ import { z } from "zod";
8
+ var baseSchema = z.object({
9
+ KEYCLOAK_BASE_URL: z.string().min(1),
10
+ KEYCLOAK_REALM: z.string().min(1),
11
+ READ_ONLY: z.string().optional(),
12
+ ALLOWED_REALMS: z.string().optional()
13
+ });
14
+ var authSchema = z.discriminatedUnion("AUTH_MODE", [
15
+ z.object({
16
+ AUTH_MODE: z.literal("service_account"),
17
+ KC_CLIENT_ID: z.string().min(1),
18
+ KC_CLIENT_SECRET: z.string().min(1)
19
+ }),
20
+ z.object({
21
+ AUTH_MODE: z.literal("password"),
22
+ KC_ADMIN_USERNAME: z.string().min(1),
23
+ KC_ADMIN_PASSWORD: z.string().min(1),
24
+ KC_ADMIN_REALM: z.string().min(1).default("master")
25
+ })
26
+ ]);
27
+ var schema = z.intersection(baseSchema, authSchema);
28
+ function parseRealms(raw) {
29
+ if (raw === void 0) {
30
+ return [];
31
+ }
32
+ return raw.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
33
+ }
34
+ function toAuthConfig(parsed) {
35
+ if (parsed.AUTH_MODE === "service_account") {
36
+ return {
37
+ mode: "service_account",
38
+ clientId: parsed.KC_CLIENT_ID,
39
+ clientSecret: parsed.KC_CLIENT_SECRET
40
+ };
41
+ }
42
+ return {
43
+ mode: "password",
44
+ username: parsed.KC_ADMIN_USERNAME,
45
+ password: parsed.KC_ADMIN_PASSWORD,
46
+ adminRealm: parsed.KC_ADMIN_REALM
47
+ };
48
+ }
49
+ function loadConfig(env) {
50
+ const result = schema.safeParse(env);
51
+ if (!result.success) {
52
+ const details = result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
53
+ throw new Error(`Invalid configuration: ${details}`);
54
+ }
55
+ const parsed = result.data;
56
+ return {
57
+ baseUrl: parsed.KEYCLOAK_BASE_URL,
58
+ realm: parsed.KEYCLOAK_REALM,
59
+ readOnly: parsed.READ_ONLY === "true",
60
+ allowedRealms: parseRealms(parsed.ALLOWED_REALMS),
61
+ auth: toAuthConfig(parsed)
62
+ };
63
+ }
64
+
65
+ // src/server.ts
66
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
67
+
68
+ // src/domain/shared/guards.ts
69
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
70
+ function ensureNonBlank(value, label) {
71
+ const trimmed = value.trim();
72
+ if (trimmed.length === 0) {
73
+ throw new Error(`${label} cannot be empty`);
74
+ }
75
+ return trimmed;
76
+ }
77
+ function ensureUuid(value, label) {
78
+ const trimmed = value.trim();
79
+ if (!UUID_PATTERN.test(trimmed)) {
80
+ throw new Error(`${label} must be a valid UUID`);
81
+ }
82
+ return trimmed;
83
+ }
84
+
85
+ // src/domain/shared/realm-name.ts
86
+ var RealmName = class _RealmName {
87
+ constructor(value) {
88
+ this.value = value;
89
+ }
90
+ value;
91
+ static fromString(value) {
92
+ return new _RealmName(ensureNonBlank(value, "RealmName"));
93
+ }
94
+ toString() {
95
+ return this.value;
96
+ }
97
+ equals(other) {
98
+ return this.value === other.value;
99
+ }
100
+ };
101
+
102
+ // src/domain/policy/realm-access-policy.ts
103
+ var RealmAccessPolicy = class _RealmAccessPolicy {
104
+ constructor(allowed) {
105
+ this.allowed = allowed;
106
+ }
107
+ allowed;
108
+ static of(allowed) {
109
+ return new _RealmAccessPolicy(allowed);
110
+ }
111
+ assertAllowed(realm) {
112
+ if (this.allowed.length === 0) {
113
+ return;
114
+ }
115
+ if (!this.allowed.some((candidate) => candidate.equals(realm))) {
116
+ throw new Error(`Realm "${realm.toString()}" is not allowed`);
117
+ }
118
+ }
119
+ };
120
+
121
+ // src/domain/policy/tool-access-policy.ts
122
+ var ToolAccessPolicy = class _ToolAccessPolicy {
123
+ constructor(readOnly) {
124
+ this.readOnly = readOnly;
125
+ }
126
+ readOnly;
127
+ static of(readOnly) {
128
+ return new _ToolAccessPolicy(readOnly);
129
+ }
130
+ isBlocked(level) {
131
+ return this.readOnly && level !== "read" /* Read */;
132
+ }
133
+ };
134
+
135
+ // src/infrastructure/auth/caching-token-provider.ts
136
+ var REFRESH_THRESHOLD_MS = 3e4;
137
+ var CachingTokenProvider = class {
138
+ constructor(fetchToken, clock) {
139
+ this.fetchToken = fetchToken;
140
+ this.clock = clock;
141
+ }
142
+ fetchToken;
143
+ clock;
144
+ cached = null;
145
+ inFlight = null;
146
+ getToken() {
147
+ const current = this.cached;
148
+ if (current !== null && !current.isExpiringWithin(REFRESH_THRESHOLD_MS, this.clock.now())) {
149
+ return Promise.resolve(current);
150
+ }
151
+ this.inFlight ??= this.refresh();
152
+ return this.inFlight;
153
+ }
154
+ refresh() {
155
+ return this.fetchToken().then((token) => {
156
+ this.cached = token;
157
+ this.inFlight = null;
158
+ return token;
159
+ }).catch((error) => {
160
+ this.inFlight = null;
161
+ throw error;
162
+ });
163
+ }
164
+ };
165
+
166
+ // src/domain/shared/access-token.ts
167
+ var AccessToken = class _AccessToken {
168
+ constructor(value, expiresAt) {
169
+ this.value = value;
170
+ this.expiresAt = expiresAt;
171
+ }
172
+ value;
173
+ expiresAt;
174
+ /**
175
+ * @param value the raw bearer token
176
+ * @param expiresAt absolute expiry, epoch milliseconds
177
+ */
178
+ static issue(value, expiresAt) {
179
+ return new _AccessToken(ensureNonBlank(value, "AccessToken"), expiresAt);
180
+ }
181
+ toString() {
182
+ return this.value;
183
+ }
184
+ isExpiringWithin(thresholdMs, now) {
185
+ return this.expiresAt - now <= thresholdMs;
186
+ }
187
+ };
188
+
189
+ // src/infrastructure/auth/token-endpoint.ts
190
+ function tokenUrl(baseUrl, realm) {
191
+ return `${baseUrl}/realms/${realm}/protocol/openid-connect/token`;
192
+ }
193
+ async function requestToken(fetchFn, url, form, clock) {
194
+ const response = await fetchFn(url, {
195
+ method: "POST",
196
+ headers: { "content-type": "application/x-www-form-urlencoded" },
197
+ body: new URLSearchParams(form).toString()
198
+ });
199
+ if (!response.ok) {
200
+ throw new Error(
201
+ `Authentication against Keycloak failed (HTTP ${String(response.status)})`
202
+ );
203
+ }
204
+ const json = await response.json();
205
+ const expiresAt = clock.now() + json.expires_in * 1e3;
206
+ return AccessToken.issue(json.access_token, expiresAt);
207
+ }
208
+
209
+ // src/infrastructure/auth/client-credentials-provider.ts
210
+ function createClientCredentialsProvider(config, fetchFn, clock) {
211
+ const url = tokenUrl(config.baseUrl, config.realm);
212
+ return new CachingTokenProvider(
213
+ () => requestToken(
214
+ fetchFn,
215
+ url,
216
+ {
217
+ grant_type: "client_credentials",
218
+ client_id: config.clientId,
219
+ client_secret: config.clientSecret
220
+ },
221
+ clock
222
+ ),
223
+ clock
224
+ );
225
+ }
226
+
227
+ // src/infrastructure/auth/password-provider.ts
228
+ function createPasswordProvider(config, fetchFn, clock) {
229
+ const url = tokenUrl(config.baseUrl, config.realm);
230
+ return new CachingTokenProvider(
231
+ () => requestToken(
232
+ fetchFn,
233
+ url,
234
+ {
235
+ grant_type: "password",
236
+ client_id: "admin-cli",
237
+ username: config.username,
238
+ password: config.password
239
+ },
240
+ clock
241
+ ),
242
+ clock
243
+ );
244
+ }
245
+
246
+ // src/infrastructure/auth/system-clock.ts
247
+ var systemClock = {
248
+ now: () => Date.now()
249
+ };
250
+
251
+ // src/infrastructure/keycloak/errors.ts
252
+ var KeycloakError = class extends Error {
253
+ constructor(status, message) {
254
+ super(message);
255
+ this.status = status;
256
+ this.name = "KeycloakError";
257
+ }
258
+ status;
259
+ };
260
+ function extractDetail(body) {
261
+ if (typeof body !== "object" || body === null) {
262
+ return null;
263
+ }
264
+ const record = body;
265
+ const candidate = record.errorMessage ?? record.error_description ?? record.error;
266
+ return typeof candidate === "string" ? candidate : null;
267
+ }
268
+ function toReadableKeycloakError(status, body) {
269
+ const detail = extractDetail(body);
270
+ switch (status) {
271
+ case 401:
272
+ return new KeycloakError(401, "Authentication failed");
273
+ case 403:
274
+ return new KeycloakError(
275
+ 403,
276
+ "Permission denied: the configured credentials lack the required role"
277
+ );
278
+ case 404:
279
+ return new KeycloakError(404, "Resource not found");
280
+ case 409:
281
+ return new KeycloakError(
282
+ 409,
283
+ detail !== null ? `Conflict: ${detail}` : "Conflict: the resource already exists"
284
+ );
285
+ default:
286
+ return new KeycloakError(
287
+ status,
288
+ detail !== null ? `Keycloak request failed: ${detail}` : `Keycloak request failed (HTTP ${String(status)})`
289
+ );
290
+ }
291
+ }
292
+
293
+ // src/infrastructure/keycloak/admin-client.ts
294
+ var DEFAULT_PAGE_SIZE = 100;
295
+ var KeycloakAdminClient = class {
296
+ constructor(config, tokens, fetchFn) {
297
+ this.config = config;
298
+ this.tokens = tokens;
299
+ this.fetchFn = fetchFn;
300
+ }
301
+ config;
302
+ tokens;
303
+ fetchFn;
304
+ async getJson(path, query = {}) {
305
+ const response = await this.send("GET", path, query);
306
+ return await response.json();
307
+ }
308
+ async post(path, body) {
309
+ await this.send("POST", path, {}, body);
310
+ }
311
+ async postJson(path, body) {
312
+ const response = await this.send("POST", path, {}, body);
313
+ return await response.json();
314
+ }
315
+ async put(path, body) {
316
+ await this.send("PUT", path, {}, body);
317
+ }
318
+ async delete(path, body) {
319
+ await this.send("DELETE", path, {}, body);
320
+ }
321
+ /** Fetches every page of a list endpoint using Keycloak's first/max paging. */
322
+ async list(path, query = {}, pageSize = DEFAULT_PAGE_SIZE) {
323
+ const all = [];
324
+ let first = 0;
325
+ for (; ; ) {
326
+ const page = await this.getJson(path, {
327
+ ...query,
328
+ first: String(first),
329
+ max: String(pageSize)
330
+ });
331
+ all.push(...page);
332
+ if (page.length < pageSize) {
333
+ break;
334
+ }
335
+ first += pageSize;
336
+ }
337
+ return all;
338
+ }
339
+ /** GET a non-realm-scoped admin resource, e.g. `/serverinfo`. */
340
+ async getAdminJson(adminPath) {
341
+ const response = await this.sendUrl(
342
+ "GET",
343
+ `${this.config.baseUrl}/admin${adminPath}`
344
+ );
345
+ return await response.json();
346
+ }
347
+ url(path, query) {
348
+ const base = `${this.config.baseUrl}/admin/realms/${this.config.realm}${path}`;
349
+ const params = new URLSearchParams(query).toString();
350
+ return params.length === 0 ? base : `${base}?${params}`;
351
+ }
352
+ send(method, path, query, body) {
353
+ return this.sendUrl(method, this.url(path, query), body);
354
+ }
355
+ async sendUrl(method, url, body, attempt = 0) {
356
+ const token = await this.tokens.getToken();
357
+ const headers = {
358
+ authorization: `Bearer ${token.toString()}`
359
+ };
360
+ const init = { method, headers };
361
+ if (body !== void 0) {
362
+ headers["content-type"] = "application/json";
363
+ init.body = JSON.stringify(body);
364
+ }
365
+ const response = await this.fetchFn(url, init);
366
+ if (response.status === 401 && attempt === 0) {
367
+ return this.sendUrl(method, url, body, attempt + 1);
368
+ }
369
+ if (!response.ok) {
370
+ throw toReadableKeycloakError(response.status, await safeJson(response));
371
+ }
372
+ return response;
373
+ }
374
+ };
375
+ async function safeJson(response) {
376
+ try {
377
+ return await response.json();
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+
383
+ // src/domain/shared/client-id.ts
384
+ var ClientId = class _ClientId {
385
+ constructor(value) {
386
+ this.value = value;
387
+ }
388
+ value;
389
+ static fromString(value) {
390
+ return new _ClientId(ensureNonBlank(value, "ClientId"));
391
+ }
392
+ toString() {
393
+ return this.value;
394
+ }
395
+ equals(other) {
396
+ return this.value === other.value;
397
+ }
398
+ };
399
+
400
+ // src/domain/shared/client-secret.ts
401
+ var ClientSecret = class _ClientSecret {
402
+ constructor(value) {
403
+ this.value = value;
404
+ }
405
+ value;
406
+ static fromString(value) {
407
+ return new _ClientSecret(ensureNonBlank(value, "ClientSecret"));
408
+ }
409
+ /** Reveals the raw secret. Only call when the caller explicitly opted in. */
410
+ reveal() {
411
+ return this.value;
412
+ }
413
+ /** A safe representation that never exposes the secret. */
414
+ masked() {
415
+ return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
416
+ }
417
+ toString() {
418
+ return this.masked();
419
+ }
420
+ };
421
+
422
+ // src/domain/shared/client-uuid.ts
423
+ var ClientUuid = class _ClientUuid {
424
+ constructor(value) {
425
+ this.value = value;
426
+ }
427
+ value;
428
+ static fromString(value) {
429
+ return new _ClientUuid(ensureUuid(value, "ClientUuid"));
430
+ }
431
+ toString() {
432
+ return this.value;
433
+ }
434
+ equals(other) {
435
+ return this.value === other.value;
436
+ }
437
+ };
438
+
439
+ // src/infrastructure/keycloak/client-repository.ts
440
+ function toSummary(raw) {
441
+ return {
442
+ uuid: ClientUuid.fromString(raw.id),
443
+ clientId: ClientId.fromString(raw.clientId),
444
+ enabled: raw.enabled ?? false,
445
+ publicClient: raw.publicClient ?? false
446
+ };
447
+ }
448
+ var KeycloakClientRepository = class {
449
+ constructor(client) {
450
+ this.client = client;
451
+ }
452
+ client;
453
+ async list() {
454
+ const raw = await this.client.getJson("/clients");
455
+ return raw.map(toSummary);
456
+ }
457
+ async findByClientId(clientId) {
458
+ const raw = await this.client.getJson("/clients", {
459
+ clientId: clientId.toString()
460
+ });
461
+ const first = raw[0];
462
+ return first === void 0 ? null : toSummary(first);
463
+ }
464
+ async getSecret(uuid) {
465
+ const raw = await this.client.getJson(
466
+ `/clients/${uuid.toString()}/client-secret`
467
+ );
468
+ return ClientSecret.fromString(raw.value);
469
+ }
470
+ async regenerateSecret(uuid) {
471
+ const raw = await this.client.postJson(
472
+ `/clients/${uuid.toString()}/client-secret`
473
+ );
474
+ return ClientSecret.fromString(raw.value);
475
+ }
476
+ };
477
+
478
+ // src/infrastructure/keycloak/authentication-repository.ts
479
+ var KeycloakAuthenticationRepository = class {
480
+ constructor(client) {
481
+ this.client = client;
482
+ }
483
+ client;
484
+ async listFlows() {
485
+ const raw = await this.client.getJson(
486
+ "/authentication/flows"
487
+ );
488
+ return raw.map((flow) => ({
489
+ id: flow.id ?? "",
490
+ alias: flow.alias ?? "",
491
+ builtIn: flow.builtIn ?? false
492
+ }));
493
+ }
494
+ async listRequiredActions() {
495
+ const raw = await this.client.getJson(
496
+ "/authentication/required-actions"
497
+ );
498
+ return raw.map((action) => ({
499
+ alias: action.alias ?? "",
500
+ name: action.name ?? "",
501
+ enabled: action.enabled ?? false,
502
+ defaultAction: action.defaultAction ?? false
503
+ }));
504
+ }
505
+ async setRequiredActionEnabled(alias, enabled) {
506
+ const current = await this.client.getJson(
507
+ `/authentication/required-actions/${encodeURIComponent(alias)}`
508
+ );
509
+ await this.client.put(
510
+ `/authentication/required-actions/${encodeURIComponent(alias)}`,
511
+ { ...current, enabled }
512
+ );
513
+ }
514
+ };
515
+
516
+ // src/infrastructure/keycloak/authorization-repository.ts
517
+ function toEntry(raw) {
518
+ return {
519
+ id: raw.id ?? raw._id ?? "",
520
+ name: raw.name ?? "",
521
+ type: raw.type ?? ""
522
+ };
523
+ }
524
+ var KeycloakAuthorizationRepository = class {
525
+ constructor(client) {
526
+ this.client = client;
527
+ }
528
+ client;
529
+ base(clientUuid) {
530
+ return `/clients/${clientUuid.toString()}/authz/resource-server`;
531
+ }
532
+ async resources(clientUuid) {
533
+ const raw = await this.client.getJson(
534
+ `${this.base(clientUuid)}/resource`
535
+ );
536
+ return raw.map(toEntry);
537
+ }
538
+ async policies(clientUuid) {
539
+ const raw = await this.client.getJson(
540
+ `${this.base(clientUuid)}/policy`
541
+ );
542
+ return raw.map(toEntry);
543
+ }
544
+ async permissions(clientUuid) {
545
+ const raw = await this.client.getJson(
546
+ `${this.base(clientUuid)}/permission`
547
+ );
548
+ return raw.map(toEntry);
549
+ }
550
+ };
551
+
552
+ // src/domain/shared/client-scope-id.ts
553
+ var ClientScopeId = class _ClientScopeId {
554
+ constructor(value) {
555
+ this.value = value;
556
+ }
557
+ value;
558
+ static fromString(value) {
559
+ return new _ClientScopeId(ensureUuid(value, "ClientScopeId"));
560
+ }
561
+ toString() {
562
+ return this.value;
563
+ }
564
+ equals(other) {
565
+ return this.value === other.value;
566
+ }
567
+ };
568
+
569
+ // src/domain/shared/client-scope-name.ts
570
+ var ClientScopeName = class _ClientScopeName {
571
+ constructor(value) {
572
+ this.value = value;
573
+ }
574
+ value;
575
+ static fromString(value) {
576
+ return new _ClientScopeName(ensureNonBlank(value, "ClientScopeName"));
577
+ }
578
+ toString() {
579
+ return this.value;
580
+ }
581
+ equals(other) {
582
+ return this.value === other.value;
583
+ }
584
+ };
585
+
586
+ // src/infrastructure/keycloak/client-scope-repository.ts
587
+ function toScope(raw) {
588
+ return {
589
+ id: ClientScopeId.fromString(raw.id),
590
+ name: ClientScopeName.fromString(raw.name),
591
+ protocol: raw.protocol ?? "openid-connect"
592
+ };
593
+ }
594
+ function toMapper(raw) {
595
+ return {
596
+ id: raw.id,
597
+ name: raw.name,
598
+ protocol: raw.protocol ?? "openid-connect",
599
+ type: raw.protocolMapper ?? "unknown"
600
+ };
601
+ }
602
+ var KeycloakClientScopeRepository = class {
603
+ constructor(client) {
604
+ this.client = client;
605
+ }
606
+ client;
607
+ async listScopes() {
608
+ const raw = await this.client.getJson("/client-scopes");
609
+ return raw.map(toScope);
610
+ }
611
+ async findScopeByName(name) {
612
+ const scopes = await this.listScopes();
613
+ return scopes.find((scope) => scope.name.equals(name)) ?? null;
614
+ }
615
+ async defaultScopes(clientUuid) {
616
+ const raw = await this.client.getJson(
617
+ `/clients/${clientUuid.toString()}/default-client-scopes`
618
+ );
619
+ return raw.map(toScope);
620
+ }
621
+ assignDefaultScope(clientUuid, scopeId) {
622
+ return this.client.put(
623
+ `/clients/${clientUuid.toString()}/default-client-scopes/${scopeId.toString()}`
624
+ );
625
+ }
626
+ removeDefaultScope(clientUuid, scopeId) {
627
+ return this.client.delete(
628
+ `/clients/${clientUuid.toString()}/default-client-scopes/${scopeId.toString()}`
629
+ );
630
+ }
631
+ async listMappers(clientUuid) {
632
+ const raw = await this.client.getJson(
633
+ `/clients/${clientUuid.toString()}/protocol-mappers/models`
634
+ );
635
+ return raw.map(toMapper);
636
+ }
637
+ };
638
+
639
+ // src/infrastructure/keycloak/event-log.ts
640
+ function toLoginEvent(raw) {
641
+ return {
642
+ time: raw.time ?? 0,
643
+ type: raw.type ?? "UNKNOWN",
644
+ userId: raw.userId ?? null,
645
+ ipAddress: raw.ipAddress ?? null
646
+ };
647
+ }
648
+ function toAdminEvent(raw) {
649
+ return {
650
+ time: raw.time ?? 0,
651
+ operationType: raw.operationType ?? "UNKNOWN",
652
+ resourceType: raw.resourceType ?? "UNKNOWN",
653
+ resourcePath: raw.resourcePath ?? null
654
+ };
655
+ }
656
+ function toParams(query) {
657
+ const params = { max: String(query.max) };
658
+ if (query.type !== void 0) {
659
+ params.type = query.type;
660
+ }
661
+ if (query.user !== void 0) {
662
+ params.user = query.user;
663
+ }
664
+ return params;
665
+ }
666
+ var KeycloakEventLog = class {
667
+ constructor(client) {
668
+ this.client = client;
669
+ }
670
+ client;
671
+ async loginEvents(query) {
672
+ const raw = await this.client.getJson(
673
+ "/events",
674
+ toParams(query)
675
+ );
676
+ return raw.map(toLoginEvent);
677
+ }
678
+ async adminEvents(query) {
679
+ const raw = await this.client.getJson(
680
+ "/admin-events",
681
+ {
682
+ max: String(query.max)
683
+ }
684
+ );
685
+ return raw.map(toAdminEvent);
686
+ }
687
+ };
688
+
689
+ // src/domain/shared/component-id.ts
690
+ var ComponentId = class _ComponentId {
691
+ constructor(value) {
692
+ this.value = value;
693
+ }
694
+ value;
695
+ static fromString(value) {
696
+ return new _ComponentId(ensureUuid(value, "ComponentId"));
697
+ }
698
+ toString() {
699
+ return this.value;
700
+ }
701
+ equals(other) {
702
+ return this.value === other.value;
703
+ }
704
+ };
705
+
706
+ // src/infrastructure/keycloak/federation-repository.ts
707
+ var PROVIDER_TYPE = "org.keycloak.storage.UserStorageProvider";
708
+ function toProvider(raw) {
709
+ return {
710
+ id: ComponentId.fromString(raw.id),
711
+ name: raw.name ?? "unnamed",
712
+ providerId: raw.providerId ?? "unknown"
713
+ };
714
+ }
715
+ var KeycloakFederationRepository = class {
716
+ constructor(client) {
717
+ this.client = client;
718
+ }
719
+ client;
720
+ async list() {
721
+ const raw = await this.client.getJson("/components", {
722
+ type: PROVIDER_TYPE
723
+ });
724
+ return raw.map(toProvider);
725
+ }
726
+ async find(id) {
727
+ try {
728
+ const raw = await this.client.getJson(
729
+ `/components/${id.toString()}`
730
+ );
731
+ return toProvider(raw);
732
+ } catch (error) {
733
+ if (error instanceof KeycloakError && error.status === 404) {
734
+ return null;
735
+ }
736
+ throw error;
737
+ }
738
+ }
739
+ async sync(id, mode) {
740
+ const action = mode === "full" ? "triggerFullSync" : "triggerChangedUsersSync";
741
+ const raw = await this.client.postJson(
742
+ `/user-storage/${id.toString()}/sync?action=${action}`
743
+ );
744
+ return {
745
+ status: raw.status ?? "completed",
746
+ added: raw.added ?? 0,
747
+ updated: raw.updated ?? 0,
748
+ removed: raw.removed ?? 0
749
+ };
750
+ }
751
+ };
752
+
753
+ // src/domain/shared/group-id.ts
754
+ var GroupId = class _GroupId {
755
+ constructor(value) {
756
+ this.value = value;
757
+ }
758
+ value;
759
+ static fromString(value) {
760
+ return new _GroupId(ensureUuid(value, "GroupId"));
761
+ }
762
+ toString() {
763
+ return this.value;
764
+ }
765
+ equals(other) {
766
+ return this.value === other.value;
767
+ }
768
+ };
769
+
770
+ // src/domain/shared/group-name.ts
771
+ var GroupName = class _GroupName {
772
+ constructor(value) {
773
+ this.value = value;
774
+ }
775
+ value;
776
+ static fromString(value) {
777
+ return new _GroupName(ensureNonBlank(value, "GroupName"));
778
+ }
779
+ toString() {
780
+ return this.value;
781
+ }
782
+ equals(other) {
783
+ return this.value === other.value;
784
+ }
785
+ };
786
+
787
+ // src/domain/shared/email.ts
788
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
789
+ var Email = class _Email {
790
+ constructor(value) {
791
+ this.value = value;
792
+ }
793
+ value;
794
+ static fromString(value) {
795
+ const trimmed = value.trim().toLowerCase();
796
+ if (!EMAIL_PATTERN.test(trimmed)) {
797
+ throw new Error(`Invalid email address: ${value}`);
798
+ }
799
+ return new _Email(trimmed);
800
+ }
801
+ toString() {
802
+ return this.value;
803
+ }
804
+ equals(other) {
805
+ return this.value === other.value;
806
+ }
807
+ };
808
+
809
+ // src/domain/shared/user-id.ts
810
+ var UserId = class _UserId {
811
+ constructor(value) {
812
+ this.value = value;
813
+ }
814
+ value;
815
+ static fromString(value) {
816
+ return new _UserId(ensureUuid(value, "UserId"));
817
+ }
818
+ toString() {
819
+ return this.value;
820
+ }
821
+ equals(other) {
822
+ return this.value === other.value;
823
+ }
824
+ };
825
+
826
+ // src/domain/shared/username.ts
827
+ var Username = class _Username {
828
+ constructor(value) {
829
+ this.value = value;
830
+ }
831
+ value;
832
+ static fromString(value) {
833
+ const trimmed = ensureNonBlank(value, "Username");
834
+ if (/\s/.test(trimmed)) {
835
+ throw new Error("Username cannot contain whitespace");
836
+ }
837
+ return new _Username(trimmed);
838
+ }
839
+ toString() {
840
+ return this.value;
841
+ }
842
+ equals(other) {
843
+ return this.value === other.value;
844
+ }
845
+ };
846
+
847
+ // src/infrastructure/keycloak/user-repository.ts
848
+ function toUser(raw) {
849
+ return {
850
+ id: UserId.fromString(raw.id),
851
+ username: Username.fromString(raw.username),
852
+ email: raw.email === void 0 || raw.email === "" ? null : Email.fromString(raw.email),
853
+ enabled: raw.enabled ?? false
854
+ };
855
+ }
856
+ var KeycloakUserRepository = class {
857
+ constructor(client) {
858
+ this.client = client;
859
+ }
860
+ client;
861
+ search(criteria) {
862
+ const query = {
863
+ first: String(criteria.first),
864
+ max: String(criteria.max)
865
+ };
866
+ if (criteria.email !== void 0) {
867
+ query.email = criteria.email.toString();
868
+ }
869
+ if (criteria.username !== void 0) {
870
+ query.username = criteria.username.toString();
871
+ }
872
+ if (criteria.search !== void 0) {
873
+ query.search = criteria.search;
874
+ }
875
+ return this.client.getJson("/users", query).then((raw) => raw.map(toUser));
876
+ }
877
+ async findById(id) {
878
+ try {
879
+ const raw = await this.client.getJson(
880
+ `/users/${id.toString()}`
881
+ );
882
+ return toUser(raw);
883
+ } catch (error) {
884
+ if (error instanceof KeycloakError && error.status === 404) {
885
+ return null;
886
+ }
887
+ throw error;
888
+ }
889
+ }
890
+ create(user) {
891
+ return this.client.post("/users", {
892
+ username: user.username.toString(),
893
+ ...user.email === void 0 ? {} : { email: user.email.toString() },
894
+ enabled: user.enabled,
895
+ emailVerified: user.emailVerified
896
+ });
897
+ }
898
+ update(id, changes) {
899
+ const body = {};
900
+ if (changes.email !== void 0) {
901
+ body.email = changes.email.toString();
902
+ }
903
+ if (changes.firstName !== void 0) {
904
+ body.firstName = changes.firstName;
905
+ }
906
+ if (changes.lastName !== void 0) {
907
+ body.lastName = changes.lastName;
908
+ }
909
+ if (changes.enabled !== void 0) {
910
+ body.enabled = changes.enabled;
911
+ }
912
+ return this.client.put(`/users/${id.toString()}`, body);
913
+ }
914
+ setEnabled(id, enabled) {
915
+ return this.client.put(`/users/${id.toString()}`, { enabled });
916
+ }
917
+ resetPassword(id, password, temporary) {
918
+ return this.client.put(`/users/${id.toString()}/reset-password`, {
919
+ type: "password",
920
+ value: password.reveal(),
921
+ temporary
922
+ });
923
+ }
924
+ sendActionsEmail(id, actions) {
925
+ return this.client.put(
926
+ `/users/${id.toString()}/execute-actions-email`,
927
+ actions.map((action) => action.toString())
928
+ );
929
+ }
930
+ logout(id) {
931
+ return this.client.post(`/users/${id.toString()}/logout`);
932
+ }
933
+ delete(id) {
934
+ return this.client.delete(`/users/${id.toString()}`);
935
+ }
936
+ async listSessions(id) {
937
+ const raw = await this.client.getJson(
938
+ `/users/${id.toString()}/sessions`
939
+ );
940
+ return raw.map((session) => ({
941
+ id: session.id ?? "",
942
+ ipAddress: session.ipAddress ?? null,
943
+ start: session.start ?? 0,
944
+ lastAccess: session.lastAccess ?? 0
945
+ }));
946
+ }
947
+ async countActiveSessions(id) {
948
+ return (await this.listSessions(id)).length;
949
+ }
950
+ };
951
+
952
+ // src/infrastructure/keycloak/group-repository.ts
953
+ function toGroup(raw) {
954
+ return {
955
+ id: GroupId.fromString(raw.id),
956
+ name: GroupName.fromString(raw.name),
957
+ path: raw.path ?? `/${raw.name}`
958
+ };
959
+ }
960
+ var KeycloakGroupRepository = class {
961
+ constructor(client) {
962
+ this.client = client;
963
+ }
964
+ client;
965
+ async list() {
966
+ const raw = await this.client.getJson("/groups");
967
+ return raw.map(toGroup);
968
+ }
969
+ create(name) {
970
+ return this.client.post("/groups", { name: name.toString() });
971
+ }
972
+ delete(id) {
973
+ return this.client.delete(`/groups/${id.toString()}`);
974
+ }
975
+ addMember(groupId, userId) {
976
+ return this.client.put(
977
+ `/users/${userId.toString()}/groups/${groupId.toString()}`
978
+ );
979
+ }
980
+ removeMember(groupId, userId) {
981
+ return this.client.delete(
982
+ `/users/${userId.toString()}/groups/${groupId.toString()}`
983
+ );
984
+ }
985
+ assignRealmRole(groupId, role) {
986
+ return this.client.post(
987
+ `/groups/${groupId.toString()}/role-mappings/realm`,
988
+ [{ id: role.id, name: role.name.toString() }]
989
+ );
990
+ }
991
+ async members(groupId) {
992
+ const raw = await this.client.getJson(`/groups/${groupId.toString()}/members`);
993
+ return raw.map(toUser);
994
+ }
995
+ async userGroups(userId) {
996
+ const raw = await this.client.getJson(`/users/${userId.toString()}/groups`);
997
+ return raw.map(toGroup);
998
+ }
999
+ };
1000
+
1001
+ // src/domain/shared/idp-alias.ts
1002
+ var IdpAlias = class _IdpAlias {
1003
+ constructor(value) {
1004
+ this.value = value;
1005
+ }
1006
+ value;
1007
+ static fromString(value) {
1008
+ return new _IdpAlias(ensureNonBlank(value, "IdpAlias"));
1009
+ }
1010
+ toString() {
1011
+ return this.value;
1012
+ }
1013
+ equals(other) {
1014
+ return this.value === other.value;
1015
+ }
1016
+ };
1017
+
1018
+ // src/infrastructure/keycloak/identity-provider-repository.ts
1019
+ function toIdp(raw) {
1020
+ return {
1021
+ alias: IdpAlias.fromString(raw.alias),
1022
+ providerId: raw.providerId ?? "unknown",
1023
+ enabled: raw.enabled ?? false,
1024
+ displayName: raw.displayName ?? null
1025
+ };
1026
+ }
1027
+ var KeycloakIdentityProviderRepository = class {
1028
+ constructor(client) {
1029
+ this.client = client;
1030
+ }
1031
+ client;
1032
+ async list() {
1033
+ const raw = await this.client.getJson(
1034
+ "/identity-provider/instances"
1035
+ );
1036
+ return raw.map(toIdp);
1037
+ }
1038
+ async find(alias) {
1039
+ try {
1040
+ const raw = await this.client.getJson(
1041
+ `/identity-provider/instances/${alias.toString()}`
1042
+ );
1043
+ return toIdp(raw);
1044
+ } catch (error) {
1045
+ if (error instanceof KeycloakError && error.status === 404) {
1046
+ return null;
1047
+ }
1048
+ throw error;
1049
+ }
1050
+ }
1051
+ create(idp) {
1052
+ return this.client.post("/identity-provider/instances", {
1053
+ alias: idp.alias.toString(),
1054
+ providerId: idp.providerId,
1055
+ enabled: idp.enabled,
1056
+ config: idp.config
1057
+ });
1058
+ }
1059
+ delete(alias) {
1060
+ return this.client.delete(
1061
+ `/identity-provider/instances/${alias.toString()}`
1062
+ );
1063
+ }
1064
+ async listMappers(alias) {
1065
+ const raw = await this.client.getJson(
1066
+ `/identity-provider/instances/${alias.toString()}/mappers`
1067
+ );
1068
+ return raw.map((mapper) => ({
1069
+ id: mapper.id,
1070
+ name: mapper.name,
1071
+ type: mapper.identityProviderMapper ?? "unknown"
1072
+ }));
1073
+ }
1074
+ };
1075
+
1076
+ // src/infrastructure/keycloak/realm-info.ts
1077
+ var KeycloakRealmInfo = class {
1078
+ constructor(client) {
1079
+ this.client = client;
1080
+ }
1081
+ client;
1082
+ getRealmConfig() {
1083
+ return this.client.getJson("");
1084
+ }
1085
+ serverInfo() {
1086
+ return this.client.getAdminJson("/serverinfo");
1087
+ }
1088
+ };
1089
+
1090
+ // src/domain/shared/role-name.ts
1091
+ var RoleName = class _RoleName {
1092
+ constructor(value) {
1093
+ this.value = value;
1094
+ }
1095
+ value;
1096
+ static fromString(value) {
1097
+ return new _RoleName(ensureNonBlank(value, "RoleName"));
1098
+ }
1099
+ toString() {
1100
+ return this.value;
1101
+ }
1102
+ equals(other) {
1103
+ return this.value === other.value;
1104
+ }
1105
+ };
1106
+
1107
+ // src/infrastructure/keycloak/role-repository.ts
1108
+ function toRole(raw) {
1109
+ return {
1110
+ id: raw.id,
1111
+ name: RoleName.fromString(raw.name),
1112
+ description: raw.description ?? null
1113
+ };
1114
+ }
1115
+ function toRepresentation(role) {
1116
+ return { id: role.id, name: role.name.toString() };
1117
+ }
1118
+ var KeycloakRoleRepository = class {
1119
+ constructor(client) {
1120
+ this.client = client;
1121
+ }
1122
+ client;
1123
+ async listRealmRoles() {
1124
+ const raw = await this.client.getJson("/roles");
1125
+ return raw.map(toRole);
1126
+ }
1127
+ async findRealmRole(name) {
1128
+ try {
1129
+ const raw = await this.client.getJson(
1130
+ `/roles/${encodeURIComponent(name.toString())}`
1131
+ );
1132
+ return toRole(raw);
1133
+ } catch (error) {
1134
+ if (error instanceof KeycloakError && error.status === 404) {
1135
+ return null;
1136
+ }
1137
+ throw error;
1138
+ }
1139
+ }
1140
+ async listUserRealmRoles(userId) {
1141
+ const raw = await this.client.getJson(
1142
+ `/users/${userId.toString()}/role-mappings/realm`
1143
+ );
1144
+ return raw.map(toRole);
1145
+ }
1146
+ assignRealmRole(userId, role) {
1147
+ return this.client.post(`/users/${userId.toString()}/role-mappings/realm`, [
1148
+ toRepresentation(role)
1149
+ ]);
1150
+ }
1151
+ removeRealmRole(userId, role) {
1152
+ return this.client.delete(
1153
+ `/users/${userId.toString()}/role-mappings/realm`,
1154
+ [toRepresentation(role)]
1155
+ );
1156
+ }
1157
+ };
1158
+
1159
+ // src/infrastructure/mcp/auth-tools.ts
1160
+ import { z as z2 } from "zod";
1161
+
1162
+ // src/application/authentication/list-auth-flows.use-case.ts
1163
+ var ListAuthFlowsUseCase = class {
1164
+ constructor(authentication) {
1165
+ this.authentication = authentication;
1166
+ }
1167
+ authentication;
1168
+ execute() {
1169
+ return this.authentication.listFlows();
1170
+ }
1171
+ };
1172
+
1173
+ // src/application/authentication/list-required-actions.use-case.ts
1174
+ var ListRequiredActionsUseCase = class {
1175
+ constructor(authentication) {
1176
+ this.authentication = authentication;
1177
+ }
1178
+ authentication;
1179
+ execute() {
1180
+ return this.authentication.listRequiredActions();
1181
+ }
1182
+ };
1183
+
1184
+ // src/application/authentication/set-required-action-enabled.use-case.ts
1185
+ var SetRequiredActionEnabledUseCase = class {
1186
+ constructor(authentication) {
1187
+ this.authentication = authentication;
1188
+ }
1189
+ authentication;
1190
+ execute(input) {
1191
+ return this.authentication.setRequiredActionEnabled(
1192
+ input.alias,
1193
+ input.enabled
1194
+ );
1195
+ }
1196
+ };
1197
+
1198
+ // src/infrastructure/mcp/tool-definition.ts
1199
+ function textResult(text) {
1200
+ return { content: [{ type: "text", text }] };
1201
+ }
1202
+
1203
+ // src/infrastructure/mcp/auth-tools.ts
1204
+ function listFlowsTool(deps) {
1205
+ return {
1206
+ name: "keycloak_auth_flows_list",
1207
+ title: "List authentication flows",
1208
+ description: "List the realm's authentication flows.",
1209
+ level: "read" /* Read */,
1210
+ inputSchema: {},
1211
+ annotations: {
1212
+ readOnlyHint: true,
1213
+ destructiveHint: false,
1214
+ idempotentHint: true
1215
+ },
1216
+ async handler() {
1217
+ const flows = await new ListAuthFlowsUseCase(
1218
+ deps.authenticationRepository
1219
+ ).execute();
1220
+ return textResult(JSON.stringify(flows, null, 2));
1221
+ }
1222
+ };
1223
+ }
1224
+ function listRequiredActionsTool(deps) {
1225
+ return {
1226
+ name: "keycloak_auth_required_actions_list",
1227
+ title: "List required actions",
1228
+ description: "List the realm's required actions.",
1229
+ level: "read" /* Read */,
1230
+ inputSchema: {},
1231
+ annotations: {
1232
+ readOnlyHint: true,
1233
+ destructiveHint: false,
1234
+ idempotentHint: true
1235
+ },
1236
+ async handler() {
1237
+ const actions = await new ListRequiredActionsUseCase(
1238
+ deps.authenticationRepository
1239
+ ).execute();
1240
+ return textResult(JSON.stringify(actions, null, 2));
1241
+ }
1242
+ };
1243
+ }
1244
+ function setRequiredActionEnabledTool(deps) {
1245
+ return {
1246
+ name: "keycloak_auth_required_action_set_enabled",
1247
+ title: "Enable or disable a required action",
1248
+ description: "Enable or disable a realm required action by alias.",
1249
+ level: "write" /* Write */,
1250
+ inputSchema: { alias: z2.string(), enabled: z2.boolean() },
1251
+ annotations: {
1252
+ readOnlyHint: false,
1253
+ destructiveHint: false,
1254
+ idempotentHint: true
1255
+ },
1256
+ async handler(args) {
1257
+ const enabled = args.enabled === true;
1258
+ await new SetRequiredActionEnabledUseCase(
1259
+ deps.authenticationRepository
1260
+ ).execute({ alias: String(args.alias), enabled });
1261
+ return textResult(
1262
+ `Required action ${String(args.alias)} ${enabled ? "enabled" : "disabled"}.`
1263
+ );
1264
+ }
1265
+ };
1266
+ }
1267
+ function buildAuthTools(deps) {
1268
+ return [
1269
+ listFlowsTool(deps),
1270
+ listRequiredActionsTool(deps),
1271
+ setRequiredActionEnabledTool(deps)
1272
+ ];
1273
+ }
1274
+
1275
+ // src/infrastructure/mcp/authz-tools.ts
1276
+ import { z as z3 } from "zod";
1277
+
1278
+ // src/application/authz/list-authz-permissions.use-case.ts
1279
+ var ListAuthzPermissionsUseCase = class {
1280
+ constructor(clients, authz) {
1281
+ this.clients = clients;
1282
+ this.authz = authz;
1283
+ }
1284
+ clients;
1285
+ authz;
1286
+ async execute(clientId) {
1287
+ const client = await this.clients.findByClientId(clientId);
1288
+ if (client === null) {
1289
+ return null;
1290
+ }
1291
+ return this.authz.permissions(client.uuid);
1292
+ }
1293
+ };
1294
+
1295
+ // src/application/authz/list-authz-policies.use-case.ts
1296
+ var ListAuthzPoliciesUseCase = class {
1297
+ constructor(clients, authz) {
1298
+ this.clients = clients;
1299
+ this.authz = authz;
1300
+ }
1301
+ clients;
1302
+ authz;
1303
+ async execute(clientId) {
1304
+ const client = await this.clients.findByClientId(clientId);
1305
+ if (client === null) {
1306
+ return null;
1307
+ }
1308
+ return this.authz.policies(client.uuid);
1309
+ }
1310
+ };
1311
+
1312
+ // src/application/authz/list-authz-resources.use-case.ts
1313
+ var ListAuthzResourcesUseCase = class {
1314
+ constructor(clients, authz) {
1315
+ this.clients = clients;
1316
+ this.authz = authz;
1317
+ }
1318
+ clients;
1319
+ authz;
1320
+ async execute(clientId) {
1321
+ const client = await this.clients.findByClientId(clientId);
1322
+ if (client === null) {
1323
+ return null;
1324
+ }
1325
+ return this.authz.resources(client.uuid);
1326
+ }
1327
+ };
1328
+
1329
+ // src/infrastructure/mcp/authz-tools.ts
1330
+ function listTool(name, title, description, run) {
1331
+ return {
1332
+ name,
1333
+ title,
1334
+ description,
1335
+ level: "read" /* Read */,
1336
+ inputSchema: { clientId: z3.string() },
1337
+ annotations: {
1338
+ readOnlyHint: true,
1339
+ destructiveHint: false,
1340
+ idempotentHint: true
1341
+ },
1342
+ async handler(args) {
1343
+ const entries = await run(ClientId.fromString(String(args.clientId)));
1344
+ return textResult(
1345
+ entries === null ? "Client not found." : JSON.stringify(entries, null, 2)
1346
+ );
1347
+ }
1348
+ };
1349
+ }
1350
+ function buildAuthzTools(deps) {
1351
+ return [
1352
+ listTool(
1353
+ "keycloak_authz_resources_list",
1354
+ "List authorization resources",
1355
+ "List a client's authorization-services resources.",
1356
+ (clientId) => new ListAuthzResourcesUseCase(
1357
+ deps.clientRepository,
1358
+ deps.authorizationRepository
1359
+ ).execute(clientId)
1360
+ ),
1361
+ listTool(
1362
+ "keycloak_authz_policies_list",
1363
+ "List authorization policies",
1364
+ "List a client's authorization-services policies.",
1365
+ (clientId) => new ListAuthzPoliciesUseCase(
1366
+ deps.clientRepository,
1367
+ deps.authorizationRepository
1368
+ ).execute(clientId)
1369
+ ),
1370
+ listTool(
1371
+ "keycloak_authz_permissions_list",
1372
+ "List authorization permissions",
1373
+ "List a client's authorization-services permissions.",
1374
+ (clientId) => new ListAuthzPermissionsUseCase(
1375
+ deps.clientRepository,
1376
+ deps.authorizationRepository
1377
+ ).execute(clientId)
1378
+ )
1379
+ ];
1380
+ }
1381
+
1382
+ // src/infrastructure/mcp/client-scope-tools.ts
1383
+ import { z as z4 } from "zod";
1384
+
1385
+ // src/application/clientscopes/assign-client-scope.use-case.ts
1386
+ var AssignClientScopeUseCase = class {
1387
+ constructor(clients, scopes) {
1388
+ this.clients = clients;
1389
+ this.scopes = scopes;
1390
+ }
1391
+ clients;
1392
+ scopes;
1393
+ async execute(input) {
1394
+ const client = await this.clients.findByClientId(input.clientId);
1395
+ if (client === null) {
1396
+ return { assigned: false, reason: "Client not found" };
1397
+ }
1398
+ const scope = await this.scopes.findScopeByName(input.scope);
1399
+ if (scope === null) {
1400
+ return { assigned: false, reason: "Scope not found" };
1401
+ }
1402
+ await this.scopes.assignDefaultScope(client.uuid, scope.id);
1403
+ return { assigned: true };
1404
+ }
1405
+ };
1406
+
1407
+ // src/application/clientscopes/get-client-default-scopes.use-case.ts
1408
+ var GetClientDefaultScopesUseCase = class {
1409
+ constructor(clients, scopes) {
1410
+ this.clients = clients;
1411
+ this.scopes = scopes;
1412
+ }
1413
+ clients;
1414
+ scopes;
1415
+ async execute(clientId) {
1416
+ const client = await this.clients.findByClientId(clientId);
1417
+ if (client === null) {
1418
+ return null;
1419
+ }
1420
+ return this.scopes.defaultScopes(client.uuid);
1421
+ }
1422
+ };
1423
+
1424
+ // src/application/clientscopes/list-client-mappers.use-case.ts
1425
+ var ListClientMappersUseCase = class {
1426
+ constructor(clients, scopes) {
1427
+ this.clients = clients;
1428
+ this.scopes = scopes;
1429
+ }
1430
+ clients;
1431
+ scopes;
1432
+ async execute(clientId) {
1433
+ const client = await this.clients.findByClientId(clientId);
1434
+ if (client === null) {
1435
+ return null;
1436
+ }
1437
+ return this.scopes.listMappers(client.uuid);
1438
+ }
1439
+ };
1440
+
1441
+ // src/application/clientscopes/list-client-scopes.use-case.ts
1442
+ var ListClientScopesUseCase = class {
1443
+ constructor(scopes) {
1444
+ this.scopes = scopes;
1445
+ }
1446
+ scopes;
1447
+ execute() {
1448
+ return this.scopes.listScopes();
1449
+ }
1450
+ };
1451
+
1452
+ // src/domain/policy/destructive-operation.ts
1453
+ var DestructiveOperation = class _DestructiveOperation {
1454
+ constructor(summary, impact) {
1455
+ this.summary = summary;
1456
+ this.impact = impact;
1457
+ }
1458
+ summary;
1459
+ impact;
1460
+ static of(summary, impact) {
1461
+ return new _DestructiveOperation(
1462
+ ensureNonBlank(summary, "summary"),
1463
+ ensureNonBlank(impact, "impact")
1464
+ );
1465
+ }
1466
+ describe() {
1467
+ return `${this.summary}
1468
+
1469
+ ${this.impact}`;
1470
+ }
1471
+ };
1472
+
1473
+ // src/application/clientscopes/remove-client-scope.use-case.ts
1474
+ var RemoveClientScopeUseCase = class {
1475
+ constructor(clients, scopes, confirmer) {
1476
+ this.clients = clients;
1477
+ this.scopes = scopes;
1478
+ this.confirmer = confirmer;
1479
+ }
1480
+ clients;
1481
+ scopes;
1482
+ confirmer;
1483
+ async execute(input) {
1484
+ const client = await this.clients.findByClientId(input.clientId);
1485
+ if (client === null) {
1486
+ return { removed: false, reason: "Client not found" };
1487
+ }
1488
+ const scope = await this.scopes.findScopeByName(input.scope);
1489
+ if (scope === null) {
1490
+ return { removed: false, reason: "Scope not found" };
1491
+ }
1492
+ const operation = DestructiveOperation.of(
1493
+ `Remove default scope ${input.scope.toString()} from client ${input.clientId.toString()}`,
1494
+ "The client stops emitting the claims and roles this scope contributes to its tokens."
1495
+ );
1496
+ if (!await this.confirmer.confirm(operation)) {
1497
+ return { removed: false, reason: "Operation not confirmed" };
1498
+ }
1499
+ await this.scopes.removeDefaultScope(client.uuid, scope.id);
1500
+ return { removed: true };
1501
+ }
1502
+ };
1503
+
1504
+ // src/infrastructure/mcp/client-scope-tools.ts
1505
+ function serializeScope(scope) {
1506
+ return {
1507
+ id: scope.id.toString(),
1508
+ name: scope.name.toString(),
1509
+ protocol: scope.protocol
1510
+ };
1511
+ }
1512
+ function listScopesTool(deps) {
1513
+ return {
1514
+ name: "keycloak_client_scopes_list",
1515
+ title: "List client scopes",
1516
+ description: "List the realm's client scopes.",
1517
+ level: "read" /* Read */,
1518
+ inputSchema: {},
1519
+ annotations: {
1520
+ readOnlyHint: true,
1521
+ destructiveHint: false,
1522
+ idempotentHint: true
1523
+ },
1524
+ async handler() {
1525
+ const scopes = await new ListClientScopesUseCase(
1526
+ deps.clientScopeRepository
1527
+ ).execute();
1528
+ return textResult(JSON.stringify(scopes.map(serializeScope), null, 2));
1529
+ }
1530
+ };
1531
+ }
1532
+ function defaultScopesTool(deps) {
1533
+ return {
1534
+ name: "keycloak_client_default_scopes_get",
1535
+ title: "Get a client's default scopes",
1536
+ description: "List the default client scopes assigned to a client.",
1537
+ level: "read" /* Read */,
1538
+ inputSchema: { clientId: z4.string() },
1539
+ annotations: {
1540
+ readOnlyHint: true,
1541
+ destructiveHint: false,
1542
+ idempotentHint: true
1543
+ },
1544
+ async handler(args) {
1545
+ const scopes = await new GetClientDefaultScopesUseCase(
1546
+ deps.clientRepository,
1547
+ deps.clientScopeRepository
1548
+ ).execute(ClientId.fromString(String(args.clientId)));
1549
+ return textResult(
1550
+ scopes === null ? "Client not found." : JSON.stringify(scopes.map(serializeScope), null, 2)
1551
+ );
1552
+ }
1553
+ };
1554
+ }
1555
+ function listMappersTool(deps) {
1556
+ return {
1557
+ name: "keycloak_client_mappers_list",
1558
+ title: "List client protocol mappers",
1559
+ description: "List a client's protocol mappers.",
1560
+ level: "read" /* Read */,
1561
+ inputSchema: { clientId: z4.string() },
1562
+ annotations: {
1563
+ readOnlyHint: true,
1564
+ destructiveHint: false,
1565
+ idempotentHint: true
1566
+ },
1567
+ async handler(args) {
1568
+ const mappers = await new ListClientMappersUseCase(
1569
+ deps.clientRepository,
1570
+ deps.clientScopeRepository
1571
+ ).execute(ClientId.fromString(String(args.clientId)));
1572
+ return textResult(
1573
+ mappers === null ? "Client not found." : JSON.stringify(mappers, null, 2)
1574
+ );
1575
+ }
1576
+ };
1577
+ }
1578
+ function assignScopeTool(deps) {
1579
+ return {
1580
+ name: "keycloak_client_scope_assign",
1581
+ title: "Assign a default scope to a client",
1582
+ description: "Add a default client scope to a client.",
1583
+ level: "write" /* Write */,
1584
+ inputSchema: { clientId: z4.string(), scope: z4.string() },
1585
+ annotations: {
1586
+ readOnlyHint: false,
1587
+ destructiveHint: false,
1588
+ idempotentHint: true
1589
+ },
1590
+ async handler(args) {
1591
+ const result = await new AssignClientScopeUseCase(
1592
+ deps.clientRepository,
1593
+ deps.clientScopeRepository
1594
+ ).execute({
1595
+ clientId: ClientId.fromString(String(args.clientId)),
1596
+ scope: ClientScopeName.fromString(String(args.scope))
1597
+ });
1598
+ return textResult(
1599
+ result.assigned ? `Scope "${String(args.scope)}" assigned.` : `Not assigned: ${result.reason ?? "unknown reason"}`
1600
+ );
1601
+ }
1602
+ };
1603
+ }
1604
+ function unassignScopeTool(deps) {
1605
+ return {
1606
+ name: "keycloak_client_scope_unassign",
1607
+ title: "Remove a default scope from a client",
1608
+ description: "Remove a default client scope from a client. Requires confirmation.",
1609
+ level: "destructive" /* Destructive */,
1610
+ inputSchema: {
1611
+ clientId: z4.string(),
1612
+ scope: z4.string(),
1613
+ confirm: z4.boolean().optional()
1614
+ },
1615
+ annotations: {
1616
+ readOnlyHint: false,
1617
+ destructiveHint: true,
1618
+ idempotentHint: false
1619
+ },
1620
+ async handler(args) {
1621
+ const confirmer = deps.confirmers.create(args.confirm === true);
1622
+ const result = await new RemoveClientScopeUseCase(
1623
+ deps.clientRepository,
1624
+ deps.clientScopeRepository,
1625
+ confirmer
1626
+ ).execute({
1627
+ clientId: ClientId.fromString(String(args.clientId)),
1628
+ scope: ClientScopeName.fromString(String(args.scope))
1629
+ });
1630
+ return textResult(
1631
+ result.removed ? `Scope "${String(args.scope)}" removed.` : `Not removed: ${result.reason ?? "unknown reason"}`
1632
+ );
1633
+ }
1634
+ };
1635
+ }
1636
+ function buildClientScopeTools(deps) {
1637
+ return [
1638
+ listScopesTool(deps),
1639
+ defaultScopesTool(deps),
1640
+ listMappersTool(deps),
1641
+ assignScopeTool(deps),
1642
+ unassignScopeTool(deps)
1643
+ ];
1644
+ }
1645
+
1646
+ // src/infrastructure/mcp/client-tools.ts
1647
+ import { z as z5 } from "zod";
1648
+
1649
+ // src/application/clients/get-client-secret.use-case.ts
1650
+ var GetClientSecretUseCase = class {
1651
+ constructor(clients) {
1652
+ this.clients = clients;
1653
+ }
1654
+ clients;
1655
+ async execute(clientId) {
1656
+ const client = await this.clients.findByClientId(clientId);
1657
+ if (client === null) {
1658
+ return null;
1659
+ }
1660
+ return this.clients.getSecret(client.uuid);
1661
+ }
1662
+ };
1663
+
1664
+ // src/application/clients/get-client.use-case.ts
1665
+ var GetClientUseCase = class {
1666
+ constructor(clients) {
1667
+ this.clients = clients;
1668
+ }
1669
+ clients;
1670
+ execute(clientId) {
1671
+ return this.clients.findByClientId(clientId);
1672
+ }
1673
+ };
1674
+
1675
+ // src/application/clients/list-clients.use-case.ts
1676
+ var ListClientsUseCase = class {
1677
+ constructor(clients) {
1678
+ this.clients = clients;
1679
+ }
1680
+ clients;
1681
+ execute() {
1682
+ return this.clients.list();
1683
+ }
1684
+ };
1685
+
1686
+ // src/application/clients/regenerate-client-secret.use-case.ts
1687
+ var RegenerateClientSecretUseCase = class {
1688
+ constructor(clients, confirmer) {
1689
+ this.clients = clients;
1690
+ this.confirmer = confirmer;
1691
+ }
1692
+ clients;
1693
+ confirmer;
1694
+ async execute(clientId) {
1695
+ const client = await this.clients.findByClientId(clientId);
1696
+ if (client === null) {
1697
+ return { regenerated: false, reason: "Client not found" };
1698
+ }
1699
+ const operation = DestructiveOperation.of(
1700
+ `Regenerate secret for client ${clientId.toString()}`,
1701
+ "Any service still using the old secret will fail to authenticate until it is updated with the new one."
1702
+ );
1703
+ if (!await this.confirmer.confirm(operation)) {
1704
+ return { regenerated: false, reason: "Operation not confirmed" };
1705
+ }
1706
+ const secret = await this.clients.regenerateSecret(client.uuid);
1707
+ return { regenerated: true, secret };
1708
+ }
1709
+ };
1710
+
1711
+ // src/infrastructure/mcp/client-tools.ts
1712
+ function serializeClient(client) {
1713
+ return {
1714
+ uuid: client.uuid.toString(),
1715
+ clientId: client.clientId.toString(),
1716
+ enabled: client.enabled,
1717
+ publicClient: client.publicClient
1718
+ };
1719
+ }
1720
+ function listClientsTool(deps) {
1721
+ return {
1722
+ name: "keycloak_client_list",
1723
+ title: "List clients",
1724
+ description: "List the realm clients.",
1725
+ level: "read" /* Read */,
1726
+ inputSchema: {},
1727
+ annotations: {
1728
+ readOnlyHint: true,
1729
+ destructiveHint: false,
1730
+ idempotentHint: true
1731
+ },
1732
+ async handler() {
1733
+ const clients = await new ListClientsUseCase(
1734
+ deps.clientRepository
1735
+ ).execute();
1736
+ return textResult(JSON.stringify(clients.map(serializeClient), null, 2));
1737
+ }
1738
+ };
1739
+ }
1740
+ function getClientTool(deps) {
1741
+ return {
1742
+ name: "keycloak_client_get",
1743
+ title: "Get client",
1744
+ description: "Fetch a client by its clientId.",
1745
+ level: "read" /* Read */,
1746
+ inputSchema: { clientId: z5.string() },
1747
+ annotations: {
1748
+ readOnlyHint: true,
1749
+ destructiveHint: false,
1750
+ idempotentHint: true
1751
+ },
1752
+ async handler(args) {
1753
+ const client = await new GetClientUseCase(deps.clientRepository).execute(
1754
+ ClientId.fromString(String(args.clientId))
1755
+ );
1756
+ return textResult(
1757
+ client === null ? "Client not found." : JSON.stringify(serializeClient(client), null, 2)
1758
+ );
1759
+ }
1760
+ };
1761
+ }
1762
+ function getClientSecretTool(deps) {
1763
+ return {
1764
+ name: "keycloak_client_get_secret",
1765
+ title: "Get client secret",
1766
+ description: "Read a confidential client's secret. Masked unless reveal is true.",
1767
+ level: "read" /* Read */,
1768
+ inputSchema: { clientId: z5.string(), reveal: z5.boolean().optional() },
1769
+ annotations: {
1770
+ readOnlyHint: true,
1771
+ destructiveHint: false,
1772
+ idempotentHint: true
1773
+ },
1774
+ async handler(args) {
1775
+ const secret = await new GetClientSecretUseCase(
1776
+ deps.clientRepository
1777
+ ).execute(ClientId.fromString(String(args.clientId)));
1778
+ if (secret === null) {
1779
+ return textResult("Client not found.");
1780
+ }
1781
+ return textResult(
1782
+ args.reveal === true ? secret.reveal() : secret.masked()
1783
+ );
1784
+ }
1785
+ };
1786
+ }
1787
+ function regenerateClientSecretTool(deps) {
1788
+ return {
1789
+ name: "keycloak_client_regenerate_secret",
1790
+ title: "Regenerate client secret",
1791
+ description: "Regenerate a confidential client's secret. Requires confirmation; the old secret stops working.",
1792
+ level: "destructive" /* Destructive */,
1793
+ inputSchema: { clientId: z5.string(), confirm: z5.boolean().optional() },
1794
+ annotations: {
1795
+ readOnlyHint: false,
1796
+ destructiveHint: true,
1797
+ idempotentHint: false
1798
+ },
1799
+ async handler(args) {
1800
+ const confirmer = deps.confirmers.create(args.confirm === true);
1801
+ const result = await new RegenerateClientSecretUseCase(
1802
+ deps.clientRepository,
1803
+ confirmer
1804
+ ).execute(ClientId.fromString(String(args.clientId)));
1805
+ if (!result.regenerated || result.secret === void 0) {
1806
+ return textResult(
1807
+ `Not regenerated: ${result.reason ?? "unknown reason"}`
1808
+ );
1809
+ }
1810
+ return textResult(`New secret: ${result.secret.reveal()}`);
1811
+ }
1812
+ };
1813
+ }
1814
+ function buildClientTools(deps) {
1815
+ return [
1816
+ listClientsTool(deps),
1817
+ getClientTool(deps),
1818
+ getClientSecretTool(deps),
1819
+ regenerateClientSecretTool(deps)
1820
+ ];
1821
+ }
1822
+
1823
+ // src/infrastructure/mcp/confirmation/elicitation-confirmer.ts
1824
+ var ElicitationConfirmer = class {
1825
+ constructor(elicit) {
1826
+ this.elicit = elicit;
1827
+ }
1828
+ elicit;
1829
+ async confirm(operation) {
1830
+ const response = await this.elicit({
1831
+ message: operation.describe(),
1832
+ requestedSchema: {
1833
+ type: "object",
1834
+ properties: {
1835
+ confirm: {
1836
+ type: "boolean",
1837
+ title: "Confirm",
1838
+ description: "Proceed with this irreversible operation?"
1839
+ }
1840
+ },
1841
+ required: ["confirm"]
1842
+ }
1843
+ });
1844
+ return response.action === "accept" && response.content?.confirm === true;
1845
+ }
1846
+ };
1847
+
1848
+ // src/infrastructure/mcp/confirmation/param-confirmer.ts
1849
+ var ParamConfirmer = class {
1850
+ constructor(provided) {
1851
+ this.provided = provided;
1852
+ }
1853
+ provided;
1854
+ confirm() {
1855
+ return Promise.resolve(this.provided);
1856
+ }
1857
+ };
1858
+
1859
+ // src/infrastructure/mcp/confirmation/confirmer-factory.ts
1860
+ var McpConfirmerFactory = class {
1861
+ constructor(server) {
1862
+ this.server = server;
1863
+ }
1864
+ server;
1865
+ create(providedConfirm) {
1866
+ const capabilities = this.server.server.getClientCapabilities();
1867
+ if (capabilities?.elicitation !== void 0) {
1868
+ return new ElicitationConfirmer(
1869
+ (request) => this.server.server.elicitInput(
1870
+ request
1871
+ )
1872
+ );
1873
+ }
1874
+ return new ParamConfirmer(providedConfirm);
1875
+ }
1876
+ };
1877
+
1878
+ // src/infrastructure/mcp/event-realm-tools.ts
1879
+ import { z as z6 } from "zod";
1880
+
1881
+ // src/application/events/get-admin-events.use-case.ts
1882
+ var GetAdminEventsUseCase = class {
1883
+ constructor(events) {
1884
+ this.events = events;
1885
+ }
1886
+ events;
1887
+ execute(query) {
1888
+ return this.events.adminEvents(query);
1889
+ }
1890
+ };
1891
+
1892
+ // src/application/events/get-login-events.use-case.ts
1893
+ var GetLoginEventsUseCase = class {
1894
+ constructor(events) {
1895
+ this.events = events;
1896
+ }
1897
+ events;
1898
+ execute(query) {
1899
+ return this.events.loginEvents(query);
1900
+ }
1901
+ };
1902
+
1903
+ // src/application/realm/get-realm-config.use-case.ts
1904
+ var GetRealmConfigUseCase = class {
1905
+ constructor(realm) {
1906
+ this.realm = realm;
1907
+ }
1908
+ realm;
1909
+ execute() {
1910
+ return this.realm.getRealmConfig();
1911
+ }
1912
+ };
1913
+
1914
+ // src/application/realm/get-server-info.use-case.ts
1915
+ var GetServerInfoUseCase = class {
1916
+ constructor(realm) {
1917
+ this.realm = realm;
1918
+ }
1919
+ realm;
1920
+ execute() {
1921
+ return this.realm.serverInfo();
1922
+ }
1923
+ };
1924
+
1925
+ // src/infrastructure/mcp/event-realm-tools.ts
1926
+ var REALM_FIELDS = [
1927
+ "realm",
1928
+ "enabled",
1929
+ "registrationAllowed",
1930
+ "resetPasswordAllowed",
1931
+ "verifyEmail",
1932
+ "loginWithEmailAllowed",
1933
+ "bruteForceProtected",
1934
+ "sslRequired",
1935
+ "accessTokenLifespan",
1936
+ "ssoSessionIdleTimeout",
1937
+ "ssoSessionMaxLifespan"
1938
+ ];
1939
+ var READ_ANNOTATIONS = {
1940
+ readOnlyHint: true,
1941
+ destructiveHint: false,
1942
+ idempotentHint: true
1943
+ };
1944
+ function pick(record, keys) {
1945
+ const out = {};
1946
+ for (const key of keys) {
1947
+ if (key in record) {
1948
+ out[key] = record[key];
1949
+ }
1950
+ }
1951
+ return out;
1952
+ }
1953
+ function serverSummary(info) {
1954
+ const systemInfo = info.systemInfo;
1955
+ const version = typeof systemInfo === "object" && systemInfo !== null && "version" in systemInfo ? systemInfo.version : null;
1956
+ return { keycloakVersion: version };
1957
+ }
1958
+ function eventQuery(args) {
1959
+ const query = {
1960
+ max: typeof args.max === "number" ? args.max : 20
1961
+ };
1962
+ if (typeof args.type === "string") {
1963
+ query.type = args.type;
1964
+ }
1965
+ if (typeof args.user === "string") {
1966
+ query.user = args.user;
1967
+ }
1968
+ return query;
1969
+ }
1970
+ function loginEventsTool(deps) {
1971
+ return {
1972
+ name: "keycloak_events_login",
1973
+ title: "List login events",
1974
+ description: "Read recent login events, optionally filtered.",
1975
+ level: "read" /* Read */,
1976
+ inputSchema: {
1977
+ max: z6.number().int().min(1).max(500).optional(),
1978
+ type: z6.string().optional(),
1979
+ user: z6.string().optional()
1980
+ },
1981
+ annotations: READ_ANNOTATIONS,
1982
+ async handler(args) {
1983
+ const events = await new GetLoginEventsUseCase(deps.eventLog).execute(
1984
+ eventQuery(args)
1985
+ );
1986
+ return textResult(JSON.stringify(events, null, 2));
1987
+ }
1988
+ };
1989
+ }
1990
+ function adminEventsTool(deps) {
1991
+ return {
1992
+ name: "keycloak_events_admin",
1993
+ title: "List admin events",
1994
+ description: "Read recent admin events.",
1995
+ level: "read" /* Read */,
1996
+ inputSchema: { max: z6.number().int().min(1).max(500).optional() },
1997
+ annotations: READ_ANNOTATIONS,
1998
+ async handler(args) {
1999
+ const events = await new GetAdminEventsUseCase(deps.eventLog).execute(
2000
+ eventQuery(args)
2001
+ );
2002
+ return textResult(JSON.stringify(events, null, 2));
2003
+ }
2004
+ };
2005
+ }
2006
+ function realmConfigTool(deps) {
2007
+ return {
2008
+ name: "keycloak_realm_get_config",
2009
+ title: "Get realm configuration",
2010
+ description: "Read key realm configuration flags.",
2011
+ level: "read" /* Read */,
2012
+ inputSchema: {},
2013
+ annotations: READ_ANNOTATIONS,
2014
+ async handler() {
2015
+ const config = await new GetRealmConfigUseCase(deps.realmInfo).execute();
2016
+ return textResult(JSON.stringify(pick(config, REALM_FIELDS), null, 2));
2017
+ }
2018
+ };
2019
+ }
2020
+ function serverInfoTool(deps) {
2021
+ return {
2022
+ name: "keycloak_server_info",
2023
+ title: "Get server info",
2024
+ description: "Read the Keycloak server version and profile.",
2025
+ level: "read" /* Read */,
2026
+ inputSchema: {},
2027
+ annotations: READ_ANNOTATIONS,
2028
+ async handler() {
2029
+ const info = await new GetServerInfoUseCase(deps.realmInfo).execute();
2030
+ return textResult(JSON.stringify(serverSummary(info), null, 2));
2031
+ }
2032
+ };
2033
+ }
2034
+ function buildEventRealmTools(deps) {
2035
+ return [
2036
+ loginEventsTool(deps),
2037
+ adminEventsTool(deps),
2038
+ realmConfigTool(deps),
2039
+ serverInfoTool(deps)
2040
+ ];
2041
+ }
2042
+
2043
+ // src/infrastructure/mcp/federation-tools.ts
2044
+ import { z as z7 } from "zod";
2045
+
2046
+ // src/application/federation/get-federation-provider.use-case.ts
2047
+ var GetFederationProviderUseCase = class {
2048
+ constructor(federation) {
2049
+ this.federation = federation;
2050
+ }
2051
+ federation;
2052
+ execute(id) {
2053
+ return this.federation.find(id);
2054
+ }
2055
+ };
2056
+
2057
+ // src/application/federation/list-federation-providers.use-case.ts
2058
+ var ListFederationProvidersUseCase = class {
2059
+ constructor(federation) {
2060
+ this.federation = federation;
2061
+ }
2062
+ federation;
2063
+ execute() {
2064
+ return this.federation.list();
2065
+ }
2066
+ };
2067
+
2068
+ // src/application/federation/sync-federation.use-case.ts
2069
+ var SyncFederationUseCase = class {
2070
+ constructor(federation) {
2071
+ this.federation = federation;
2072
+ }
2073
+ federation;
2074
+ execute(input) {
2075
+ return this.federation.sync(input.id, input.mode);
2076
+ }
2077
+ };
2078
+
2079
+ // src/infrastructure/mcp/federation-tools.ts
2080
+ function serializeProvider(provider) {
2081
+ return {
2082
+ id: provider.id.toString(),
2083
+ name: provider.name,
2084
+ providerId: provider.providerId
2085
+ };
2086
+ }
2087
+ function listFederationTool(deps) {
2088
+ return {
2089
+ name: "keycloak_federation_list",
2090
+ title: "List user federation providers",
2091
+ description: "List the realm's user federation (LDAP/Kerberos) providers.",
2092
+ level: "read" /* Read */,
2093
+ inputSchema: {},
2094
+ annotations: {
2095
+ readOnlyHint: true,
2096
+ destructiveHint: false,
2097
+ idempotentHint: true
2098
+ },
2099
+ async handler() {
2100
+ const providers = await new ListFederationProvidersUseCase(
2101
+ deps.federationRepository
2102
+ ).execute();
2103
+ return textResult(
2104
+ JSON.stringify(providers.map(serializeProvider), null, 2)
2105
+ );
2106
+ }
2107
+ };
2108
+ }
2109
+ function getFederationTool(deps) {
2110
+ return {
2111
+ name: "keycloak_federation_get",
2112
+ title: "Get a user federation provider",
2113
+ description: "Fetch a user federation provider by id.",
2114
+ level: "read" /* Read */,
2115
+ inputSchema: { id: z7.string() },
2116
+ annotations: {
2117
+ readOnlyHint: true,
2118
+ destructiveHint: false,
2119
+ idempotentHint: true
2120
+ },
2121
+ async handler(args) {
2122
+ const provider = await new GetFederationProviderUseCase(
2123
+ deps.federationRepository
2124
+ ).execute(ComponentId.fromString(String(args.id)));
2125
+ return textResult(
2126
+ provider === null ? "Federation provider not found." : JSON.stringify(serializeProvider(provider), null, 2)
2127
+ );
2128
+ }
2129
+ };
2130
+ }
2131
+ function syncFederationTool(deps) {
2132
+ return {
2133
+ name: "keycloak_federation_sync",
2134
+ title: "Synchronize a user federation provider",
2135
+ description: "Trigger a user sync from a federation provider (full or changed).",
2136
+ level: "write" /* Write */,
2137
+ inputSchema: {
2138
+ id: z7.string(),
2139
+ mode: z7.enum(["full", "changed"]).optional()
2140
+ },
2141
+ annotations: {
2142
+ readOnlyHint: false,
2143
+ destructiveHint: false,
2144
+ idempotentHint: false
2145
+ },
2146
+ async handler(args) {
2147
+ const mode = args.mode === "full" ? "full" : "changed";
2148
+ const result = await new SyncFederationUseCase(
2149
+ deps.federationRepository
2150
+ ).execute({ id: ComponentId.fromString(String(args.id)), mode });
2151
+ return textResult(JSON.stringify(result, null, 2));
2152
+ }
2153
+ };
2154
+ }
2155
+ function buildFederationTools(deps) {
2156
+ return [
2157
+ listFederationTool(deps),
2158
+ getFederationTool(deps),
2159
+ syncFederationTool(deps)
2160
+ ];
2161
+ }
2162
+
2163
+ // src/infrastructure/mcp/group-tools.ts
2164
+ import { z as z8 } from "zod";
2165
+
2166
+ // src/application/groups/add-group-member.use-case.ts
2167
+ var AddGroupMemberUseCase = class {
2168
+ constructor(groups) {
2169
+ this.groups = groups;
2170
+ }
2171
+ groups;
2172
+ execute(input) {
2173
+ return this.groups.addMember(input.groupId, input.userId);
2174
+ }
2175
+ };
2176
+
2177
+ // src/application/groups/assign-group-role.use-case.ts
2178
+ var AssignGroupRoleUseCase = class {
2179
+ constructor(roles, groups) {
2180
+ this.roles = roles;
2181
+ this.groups = groups;
2182
+ }
2183
+ roles;
2184
+ groups;
2185
+ async execute(input) {
2186
+ const role = await this.roles.findRealmRole(input.role);
2187
+ if (role === null) {
2188
+ return { assigned: false, reason: "Role not found" };
2189
+ }
2190
+ await this.groups.assignRealmRole(input.groupId, role);
2191
+ return { assigned: true };
2192
+ }
2193
+ };
2194
+
2195
+ // src/application/groups/create-group.use-case.ts
2196
+ var CreateGroupUseCase = class {
2197
+ constructor(groups) {
2198
+ this.groups = groups;
2199
+ }
2200
+ groups;
2201
+ execute(name) {
2202
+ return this.groups.create(name);
2203
+ }
2204
+ };
2205
+
2206
+ // src/application/groups/delete-group.use-case.ts
2207
+ var DeleteGroupUseCase = class {
2208
+ constructor(groups, confirmer) {
2209
+ this.groups = groups;
2210
+ this.confirmer = confirmer;
2211
+ }
2212
+ groups;
2213
+ confirmer;
2214
+ async execute(id) {
2215
+ const operation = DestructiveOperation.of(
2216
+ `Delete group ${id.toString()}`,
2217
+ "Removes the group, its sub-groups, and every membership and role mapping it carries. This is irreversible."
2218
+ );
2219
+ if (!await this.confirmer.confirm(operation)) {
2220
+ return { deleted: false, reason: "Operation not confirmed" };
2221
+ }
2222
+ await this.groups.delete(id);
2223
+ return { deleted: true };
2224
+ }
2225
+ };
2226
+
2227
+ // src/application/groups/list-group-members.use-case.ts
2228
+ var ListGroupMembersUseCase = class {
2229
+ constructor(groups) {
2230
+ this.groups = groups;
2231
+ }
2232
+ groups;
2233
+ execute(groupId) {
2234
+ return this.groups.members(groupId);
2235
+ }
2236
+ };
2237
+
2238
+ // src/application/groups/list-groups.use-case.ts
2239
+ var ListGroupsUseCase = class {
2240
+ constructor(groups) {
2241
+ this.groups = groups;
2242
+ }
2243
+ groups;
2244
+ execute() {
2245
+ return this.groups.list();
2246
+ }
2247
+ };
2248
+
2249
+ // src/application/groups/list-user-groups.use-case.ts
2250
+ var ListUserGroupsUseCase = class {
2251
+ constructor(groups) {
2252
+ this.groups = groups;
2253
+ }
2254
+ groups;
2255
+ execute(userId) {
2256
+ return this.groups.userGroups(userId);
2257
+ }
2258
+ };
2259
+
2260
+ // src/application/groups/remove-group-member.use-case.ts
2261
+ var RemoveGroupMemberUseCase = class {
2262
+ constructor(groups, confirmer) {
2263
+ this.groups = groups;
2264
+ this.confirmer = confirmer;
2265
+ }
2266
+ groups;
2267
+ confirmer;
2268
+ async execute(input) {
2269
+ const operation = DestructiveOperation.of(
2270
+ `Remove user ${input.userId.toString()} from group ${input.groupId.toString()}`,
2271
+ "The user loses every role and permission granted through this group."
2272
+ );
2273
+ if (!await this.confirmer.confirm(operation)) {
2274
+ return { removed: false, reason: "Operation not confirmed" };
2275
+ }
2276
+ await this.groups.removeMember(input.groupId, input.userId);
2277
+ return { removed: true };
2278
+ }
2279
+ };
2280
+
2281
+ // src/infrastructure/mcp/group-tools.ts
2282
+ function serializeGroup(group) {
2283
+ return {
2284
+ id: group.id.toString(),
2285
+ name: group.name.toString(),
2286
+ path: group.path
2287
+ };
2288
+ }
2289
+ function serializeUser(user) {
2290
+ return {
2291
+ id: user.id.toString(),
2292
+ username: user.username.toString(),
2293
+ email: user.email?.toString() ?? null,
2294
+ enabled: user.enabled
2295
+ };
2296
+ }
2297
+ function listGroupsTool(deps) {
2298
+ return {
2299
+ name: "keycloak_group_list",
2300
+ title: "List groups",
2301
+ description: "List the realm's top-level groups.",
2302
+ level: "read" /* Read */,
2303
+ inputSchema: {},
2304
+ annotations: {
2305
+ readOnlyHint: true,
2306
+ destructiveHint: false,
2307
+ idempotentHint: true
2308
+ },
2309
+ async handler() {
2310
+ const groups = await new ListGroupsUseCase(
2311
+ deps.groupRepository
2312
+ ).execute();
2313
+ return textResult(JSON.stringify(groups.map(serializeGroup), null, 2));
2314
+ }
2315
+ };
2316
+ }
2317
+ function createGroupTool(deps) {
2318
+ return {
2319
+ name: "keycloak_group_create",
2320
+ title: "Create group",
2321
+ description: "Create a top-level group.",
2322
+ level: "write" /* Write */,
2323
+ inputSchema: { name: z8.string() },
2324
+ annotations: {
2325
+ readOnlyHint: false,
2326
+ destructiveHint: false,
2327
+ idempotentHint: false
2328
+ },
2329
+ async handler(args) {
2330
+ await new CreateGroupUseCase(deps.groupRepository).execute(
2331
+ GroupName.fromString(String(args.name))
2332
+ );
2333
+ return textResult(`Group "${String(args.name)}" created.`);
2334
+ }
2335
+ };
2336
+ }
2337
+ function addGroupMemberTool(deps) {
2338
+ return {
2339
+ name: "keycloak_group_member_add",
2340
+ title: "Add group member",
2341
+ description: "Add a user to a group.",
2342
+ level: "write" /* Write */,
2343
+ inputSchema: { groupId: z8.string(), userId: z8.string() },
2344
+ annotations: {
2345
+ readOnlyHint: false,
2346
+ destructiveHint: false,
2347
+ idempotentHint: true
2348
+ },
2349
+ async handler(args) {
2350
+ await new AddGroupMemberUseCase(deps.groupRepository).execute({
2351
+ groupId: GroupId.fromString(String(args.groupId)),
2352
+ userId: UserId.fromString(String(args.userId))
2353
+ });
2354
+ return textResult("User added to group.");
2355
+ }
2356
+ };
2357
+ }
2358
+ function removeGroupMemberTool(deps) {
2359
+ return {
2360
+ name: "keycloak_group_member_remove",
2361
+ title: "Remove group member",
2362
+ description: "Remove a user from a group. Requires confirmation.",
2363
+ level: "destructive" /* Destructive */,
2364
+ inputSchema: {
2365
+ groupId: z8.string(),
2366
+ userId: z8.string(),
2367
+ confirm: z8.boolean().optional()
2368
+ },
2369
+ annotations: {
2370
+ readOnlyHint: false,
2371
+ destructiveHint: true,
2372
+ idempotentHint: false
2373
+ },
2374
+ async handler(args) {
2375
+ const confirmer = deps.confirmers.create(args.confirm === true);
2376
+ const result = await new RemoveGroupMemberUseCase(
2377
+ deps.groupRepository,
2378
+ confirmer
2379
+ ).execute({
2380
+ groupId: GroupId.fromString(String(args.groupId)),
2381
+ userId: UserId.fromString(String(args.userId))
2382
+ });
2383
+ return textResult(
2384
+ result.removed ? "User removed from group." : `Not removed: ${result.reason ?? "unknown reason"}`
2385
+ );
2386
+ }
2387
+ };
2388
+ }
2389
+ function deleteGroupTool(deps) {
2390
+ return {
2391
+ name: "keycloak_group_delete",
2392
+ title: "Delete group",
2393
+ description: "Delete a group. Requires confirmation.",
2394
+ level: "destructive" /* Destructive */,
2395
+ inputSchema: { id: z8.string(), confirm: z8.boolean().optional() },
2396
+ annotations: {
2397
+ readOnlyHint: false,
2398
+ destructiveHint: true,
2399
+ idempotentHint: false
2400
+ },
2401
+ async handler(args) {
2402
+ const confirmer = deps.confirmers.create(args.confirm === true);
2403
+ const result = await new DeleteGroupUseCase(
2404
+ deps.groupRepository,
2405
+ confirmer
2406
+ ).execute(GroupId.fromString(String(args.id)));
2407
+ return textResult(
2408
+ result.deleted ? "Group deleted." : `Not deleted: ${result.reason ?? "unknown reason"}`
2409
+ );
2410
+ }
2411
+ };
2412
+ }
2413
+ function assignGroupRoleTool(deps) {
2414
+ return {
2415
+ name: "keycloak_group_role_assign",
2416
+ title: "Assign a realm role to a group",
2417
+ description: "Grant a realm role to a group (inherited by its members).",
2418
+ level: "write" /* Write */,
2419
+ inputSchema: { groupId: z8.string(), role: z8.string() },
2420
+ annotations: {
2421
+ readOnlyHint: false,
2422
+ destructiveHint: false,
2423
+ idempotentHint: true
2424
+ },
2425
+ async handler(args) {
2426
+ const result = await new AssignGroupRoleUseCase(
2427
+ deps.roleRepository,
2428
+ deps.groupRepository
2429
+ ).execute({
2430
+ groupId: GroupId.fromString(String(args.groupId)),
2431
+ role: RoleName.fromString(String(args.role))
2432
+ });
2433
+ return textResult(
2434
+ result.assigned ? `Role "${String(args.role)}" assigned to group.` : `Not assigned: ${result.reason ?? "unknown reason"}`
2435
+ );
2436
+ }
2437
+ };
2438
+ }
2439
+ function listGroupMembersTool(deps) {
2440
+ return {
2441
+ name: "keycloak_group_members_list",
2442
+ title: "List group members",
2443
+ description: "List the users that are members of a group.",
2444
+ level: "read" /* Read */,
2445
+ inputSchema: { groupId: z8.string() },
2446
+ annotations: {
2447
+ readOnlyHint: true,
2448
+ destructiveHint: false,
2449
+ idempotentHint: true
2450
+ },
2451
+ async handler(args) {
2452
+ const members = await new ListGroupMembersUseCase(
2453
+ deps.groupRepository
2454
+ ).execute(GroupId.fromString(String(args.groupId)));
2455
+ return textResult(JSON.stringify(members.map(serializeUser), null, 2));
2456
+ }
2457
+ };
2458
+ }
2459
+ function listUserGroupsTool(deps) {
2460
+ return {
2461
+ name: "keycloak_user_groups_list",
2462
+ title: "List a user's groups",
2463
+ description: "List the groups a user belongs to.",
2464
+ level: "read" /* Read */,
2465
+ inputSchema: { userId: z8.string() },
2466
+ annotations: {
2467
+ readOnlyHint: true,
2468
+ destructiveHint: false,
2469
+ idempotentHint: true
2470
+ },
2471
+ async handler(args) {
2472
+ const groups = await new ListUserGroupsUseCase(
2473
+ deps.groupRepository
2474
+ ).execute(UserId.fromString(String(args.userId)));
2475
+ return textResult(JSON.stringify(groups.map(serializeGroup), null, 2));
2476
+ }
2477
+ };
2478
+ }
2479
+ function buildGroupTools(deps) {
2480
+ return [
2481
+ listGroupsTool(deps),
2482
+ listGroupMembersTool(deps),
2483
+ listUserGroupsTool(deps),
2484
+ createGroupTool(deps),
2485
+ addGroupMemberTool(deps),
2486
+ assignGroupRoleTool(deps),
2487
+ removeGroupMemberTool(deps),
2488
+ deleteGroupTool(deps)
2489
+ ];
2490
+ }
2491
+
2492
+ // src/infrastructure/mcp/idp-tools.ts
2493
+ import { z as z9 } from "zod";
2494
+
2495
+ // src/application/idp/create-identity-provider.use-case.ts
2496
+ var CreateIdentityProviderUseCase = class {
2497
+ constructor(idps) {
2498
+ this.idps = idps;
2499
+ }
2500
+ idps;
2501
+ execute(idp) {
2502
+ return this.idps.create(idp);
2503
+ }
2504
+ };
2505
+
2506
+ // src/application/idp/delete-identity-provider.use-case.ts
2507
+ var DeleteIdentityProviderUseCase = class {
2508
+ constructor(idps, confirmer) {
2509
+ this.idps = idps;
2510
+ this.confirmer = confirmer;
2511
+ }
2512
+ idps;
2513
+ confirmer;
2514
+ async execute(alias) {
2515
+ const operation = DestructiveOperation.of(
2516
+ `Delete identity provider ${alias.toString()}`,
2517
+ "Users who authenticate through this provider can no longer log in via it, and their federated links are removed. This is irreversible."
2518
+ );
2519
+ if (!await this.confirmer.confirm(operation)) {
2520
+ return { deleted: false, reason: "Operation not confirmed" };
2521
+ }
2522
+ await this.idps.delete(alias);
2523
+ return { deleted: true };
2524
+ }
2525
+ };
2526
+
2527
+ // src/application/idp/get-identity-provider.use-case.ts
2528
+ var GetIdentityProviderUseCase = class {
2529
+ constructor(idps) {
2530
+ this.idps = idps;
2531
+ }
2532
+ idps;
2533
+ execute(alias) {
2534
+ return this.idps.find(alias);
2535
+ }
2536
+ };
2537
+
2538
+ // src/application/idp/list-identity-providers.use-case.ts
2539
+ var ListIdentityProvidersUseCase = class {
2540
+ constructor(idps) {
2541
+ this.idps = idps;
2542
+ }
2543
+ idps;
2544
+ execute() {
2545
+ return this.idps.list();
2546
+ }
2547
+ };
2548
+
2549
+ // src/application/idp/list-idp-mappers.use-case.ts
2550
+ var ListIdpMappersUseCase = class {
2551
+ constructor(idps) {
2552
+ this.idps = idps;
2553
+ }
2554
+ idps;
2555
+ execute(alias) {
2556
+ return this.idps.listMappers(alias);
2557
+ }
2558
+ };
2559
+
2560
+ // src/infrastructure/mcp/idp-tools.ts
2561
+ function serializeIdp(idp) {
2562
+ return {
2563
+ alias: idp.alias.toString(),
2564
+ providerId: idp.providerId,
2565
+ enabled: idp.enabled,
2566
+ displayName: idp.displayName
2567
+ };
2568
+ }
2569
+ function readConfig(value) {
2570
+ const config = {};
2571
+ if (value !== null && typeof value === "object") {
2572
+ for (const [key, entry] of Object.entries(
2573
+ value
2574
+ )) {
2575
+ if (typeof entry === "string") {
2576
+ config[key] = entry;
2577
+ }
2578
+ }
2579
+ }
2580
+ return config;
2581
+ }
2582
+ function listIdpsTool(deps) {
2583
+ return {
2584
+ name: "keycloak_idp_list",
2585
+ title: "List identity providers",
2586
+ description: "List the realm's identity providers.",
2587
+ level: "read" /* Read */,
2588
+ inputSchema: {},
2589
+ annotations: {
2590
+ readOnlyHint: true,
2591
+ destructiveHint: false,
2592
+ idempotentHint: true
2593
+ },
2594
+ async handler() {
2595
+ const idps = await new ListIdentityProvidersUseCase(
2596
+ deps.identityProviderRepository
2597
+ ).execute();
2598
+ return textResult(JSON.stringify(idps.map(serializeIdp), null, 2));
2599
+ }
2600
+ };
2601
+ }
2602
+ function getIdpTool(deps) {
2603
+ return {
2604
+ name: "keycloak_idp_get",
2605
+ title: "Get identity provider",
2606
+ description: "Fetch an identity provider by alias.",
2607
+ level: "read" /* Read */,
2608
+ inputSchema: { alias: z9.string() },
2609
+ annotations: {
2610
+ readOnlyHint: true,
2611
+ destructiveHint: false,
2612
+ idempotentHint: true
2613
+ },
2614
+ async handler(args) {
2615
+ const idp = await new GetIdentityProviderUseCase(
2616
+ deps.identityProviderRepository
2617
+ ).execute(IdpAlias.fromString(String(args.alias)));
2618
+ return textResult(
2619
+ idp === null ? "Identity provider not found." : JSON.stringify(serializeIdp(idp), null, 2)
2620
+ );
2621
+ }
2622
+ };
2623
+ }
2624
+ function listIdpMappersTool(deps) {
2625
+ return {
2626
+ name: "keycloak_idp_mappers_list",
2627
+ title: "List identity provider mappers",
2628
+ description: "List an identity provider's mappers.",
2629
+ level: "read" /* Read */,
2630
+ inputSchema: { alias: z9.string() },
2631
+ annotations: {
2632
+ readOnlyHint: true,
2633
+ destructiveHint: false,
2634
+ idempotentHint: true
2635
+ },
2636
+ async handler(args) {
2637
+ const mappers = await new ListIdpMappersUseCase(
2638
+ deps.identityProviderRepository
2639
+ ).execute(IdpAlias.fromString(String(args.alias)));
2640
+ return textResult(JSON.stringify(mappers, null, 2));
2641
+ }
2642
+ };
2643
+ }
2644
+ function createIdpTool(deps) {
2645
+ return {
2646
+ name: "keycloak_idp_create",
2647
+ title: "Create identity provider",
2648
+ description: "Create an identity provider. `config` carries provider-specific keys (clientId, clientSecret, authorizationUrl, \u2026).",
2649
+ level: "write" /* Write */,
2650
+ inputSchema: {
2651
+ alias: z9.string(),
2652
+ providerId: z9.string(),
2653
+ enabled: z9.boolean().optional(),
2654
+ config: z9.record(z9.string(), z9.string()).optional()
2655
+ },
2656
+ annotations: {
2657
+ readOnlyHint: false,
2658
+ destructiveHint: false,
2659
+ idempotentHint: false
2660
+ },
2661
+ async handler(args) {
2662
+ await new CreateIdentityProviderUseCase(
2663
+ deps.identityProviderRepository
2664
+ ).execute({
2665
+ alias: IdpAlias.fromString(String(args.alias)),
2666
+ providerId: String(args.providerId),
2667
+ enabled: args.enabled !== false,
2668
+ config: readConfig(args.config)
2669
+ });
2670
+ return textResult(`Identity provider "${String(args.alias)}" created.`);
2671
+ }
2672
+ };
2673
+ }
2674
+ function deleteIdpTool(deps) {
2675
+ return {
2676
+ name: "keycloak_idp_delete",
2677
+ title: "Delete identity provider",
2678
+ description: "Delete an identity provider. Requires confirmation.",
2679
+ level: "destructive" /* Destructive */,
2680
+ inputSchema: { alias: z9.string(), confirm: z9.boolean().optional() },
2681
+ annotations: {
2682
+ readOnlyHint: false,
2683
+ destructiveHint: true,
2684
+ idempotentHint: false
2685
+ },
2686
+ async handler(args) {
2687
+ const confirmer = deps.confirmers.create(args.confirm === true);
2688
+ const result = await new DeleteIdentityProviderUseCase(
2689
+ deps.identityProviderRepository,
2690
+ confirmer
2691
+ ).execute(IdpAlias.fromString(String(args.alias)));
2692
+ return textResult(
2693
+ result.deleted ? `Identity provider "${String(args.alias)}" deleted.` : `Not deleted: ${result.reason ?? "unknown reason"}`
2694
+ );
2695
+ }
2696
+ };
2697
+ }
2698
+ function buildIdpTools(deps) {
2699
+ return [
2700
+ listIdpsTool(deps),
2701
+ getIdpTool(deps),
2702
+ listIdpMappersTool(deps),
2703
+ createIdpTool(deps),
2704
+ deleteIdpTool(deps)
2705
+ ];
2706
+ }
2707
+
2708
+ // src/infrastructure/mcp/role-tools.ts
2709
+ import { z as z10 } from "zod";
2710
+
2711
+ // src/application/roles/assign-user-role.use-case.ts
2712
+ var AssignUserRoleUseCase = class {
2713
+ constructor(roles) {
2714
+ this.roles = roles;
2715
+ }
2716
+ roles;
2717
+ async execute(input) {
2718
+ const role = await this.roles.findRealmRole(input.role);
2719
+ if (role === null) {
2720
+ return { assigned: false, reason: "Role not found" };
2721
+ }
2722
+ await this.roles.assignRealmRole(input.userId, role);
2723
+ return { assigned: true };
2724
+ }
2725
+ };
2726
+
2727
+ // src/application/roles/get-user-roles.use-case.ts
2728
+ var GetUserRolesUseCase = class {
2729
+ constructor(roles) {
2730
+ this.roles = roles;
2731
+ }
2732
+ roles;
2733
+ execute(userId) {
2734
+ return this.roles.listUserRealmRoles(userId);
2735
+ }
2736
+ };
2737
+
2738
+ // src/application/roles/list-realm-roles.use-case.ts
2739
+ var ListRealmRolesUseCase = class {
2740
+ constructor(roles) {
2741
+ this.roles = roles;
2742
+ }
2743
+ roles;
2744
+ execute() {
2745
+ return this.roles.listRealmRoles();
2746
+ }
2747
+ };
2748
+
2749
+ // src/application/roles/unassign-user-role.use-case.ts
2750
+ var UnassignUserRoleUseCase = class {
2751
+ constructor(roles, confirmer) {
2752
+ this.roles = roles;
2753
+ this.confirmer = confirmer;
2754
+ }
2755
+ roles;
2756
+ confirmer;
2757
+ async execute(input) {
2758
+ const role = await this.roles.findRealmRole(input.role);
2759
+ if (role === null) {
2760
+ return { removed: false, reason: "Role not found" };
2761
+ }
2762
+ const operation = DestructiveOperation.of(
2763
+ `Remove role ${role.name.toString()} from user ${input.userId.toString()}`,
2764
+ "The user immediately loses every permission granted by this role."
2765
+ );
2766
+ if (!await this.confirmer.confirm(operation)) {
2767
+ return { removed: false, reason: "Operation not confirmed" };
2768
+ }
2769
+ await this.roles.removeRealmRole(input.userId, role);
2770
+ return { removed: true };
2771
+ }
2772
+ };
2773
+
2774
+ // src/infrastructure/mcp/role-tools.ts
2775
+ function serializeRole(role) {
2776
+ return {
2777
+ id: role.id,
2778
+ name: role.name.toString(),
2779
+ description: role.description
2780
+ };
2781
+ }
2782
+ function listRealmRolesTool(deps) {
2783
+ return {
2784
+ name: "keycloak_role_list",
2785
+ title: "List realm roles",
2786
+ description: "List the realm roles.",
2787
+ level: "read" /* Read */,
2788
+ inputSchema: {},
2789
+ annotations: {
2790
+ readOnlyHint: true,
2791
+ destructiveHint: false,
2792
+ idempotentHint: true
2793
+ },
2794
+ async handler() {
2795
+ const roles = await new ListRealmRolesUseCase(
2796
+ deps.roleRepository
2797
+ ).execute();
2798
+ return textResult(JSON.stringify(roles.map(serializeRole), null, 2));
2799
+ }
2800
+ };
2801
+ }
2802
+ function getUserRolesTool(deps) {
2803
+ return {
2804
+ name: "keycloak_user_roles_get",
2805
+ title: "Get a user's realm roles",
2806
+ description: "List the realm roles assigned to a user.",
2807
+ level: "read" /* Read */,
2808
+ inputSchema: { userId: z10.string() },
2809
+ annotations: {
2810
+ readOnlyHint: true,
2811
+ destructiveHint: false,
2812
+ idempotentHint: true
2813
+ },
2814
+ async handler(args) {
2815
+ const roles = await new GetUserRolesUseCase(deps.roleRepository).execute(
2816
+ UserId.fromString(String(args.userId))
2817
+ );
2818
+ return textResult(JSON.stringify(roles.map(serializeRole), null, 2));
2819
+ }
2820
+ };
2821
+ }
2822
+ function assignUserRoleTool(deps) {
2823
+ return {
2824
+ name: "keycloak_user_role_assign",
2825
+ title: "Assign a realm role to a user",
2826
+ description: "Grant a realm role to a user.",
2827
+ level: "write" /* Write */,
2828
+ inputSchema: { userId: z10.string(), role: z10.string() },
2829
+ annotations: {
2830
+ readOnlyHint: false,
2831
+ destructiveHint: false,
2832
+ idempotentHint: true
2833
+ },
2834
+ async handler(args) {
2835
+ const result = await new AssignUserRoleUseCase(
2836
+ deps.roleRepository
2837
+ ).execute({
2838
+ userId: UserId.fromString(String(args.userId)),
2839
+ role: RoleName.fromString(String(args.role))
2840
+ });
2841
+ return textResult(
2842
+ result.assigned ? `Role "${String(args.role)}" assigned.` : `Not assigned: ${result.reason ?? "unknown reason"}`
2843
+ );
2844
+ }
2845
+ };
2846
+ }
2847
+ function unassignUserRoleTool(deps) {
2848
+ return {
2849
+ name: "keycloak_user_role_unassign",
2850
+ title: "Remove a realm role from a user",
2851
+ description: "Revoke a realm role from a user. Requires confirmation.",
2852
+ level: "destructive" /* Destructive */,
2853
+ inputSchema: {
2854
+ userId: z10.string(),
2855
+ role: z10.string(),
2856
+ confirm: z10.boolean().optional()
2857
+ },
2858
+ annotations: {
2859
+ readOnlyHint: false,
2860
+ destructiveHint: true,
2861
+ idempotentHint: false
2862
+ },
2863
+ async handler(args) {
2864
+ const confirmer = deps.confirmers.create(args.confirm === true);
2865
+ const result = await new UnassignUserRoleUseCase(
2866
+ deps.roleRepository,
2867
+ confirmer
2868
+ ).execute({
2869
+ userId: UserId.fromString(String(args.userId)),
2870
+ role: RoleName.fromString(String(args.role))
2871
+ });
2872
+ return textResult(
2873
+ result.removed ? `Role "${String(args.role)}" removed.` : `Not removed: ${result.reason ?? "unknown reason"}`
2874
+ );
2875
+ }
2876
+ };
2877
+ }
2878
+ function buildRoleTools(deps) {
2879
+ return [
2880
+ listRealmRolesTool(deps),
2881
+ getUserRolesTool(deps),
2882
+ assignUserRoleTool(deps),
2883
+ unassignUserRoleTool(deps)
2884
+ ];
2885
+ }
2886
+
2887
+ // src/infrastructure/mcp/tool-registry.ts
2888
+ function filterTools(definitions, policy) {
2889
+ return definitions.filter(
2890
+ (definition) => !policy.isBlocked(definition.level)
2891
+ );
2892
+ }
2893
+ function registerTools(server, definitions) {
2894
+ for (const definition of definitions) {
2895
+ server.registerTool(
2896
+ definition.name,
2897
+ {
2898
+ title: definition.title,
2899
+ description: definition.description,
2900
+ inputSchema: definition.inputSchema,
2901
+ annotations: definition.annotations
2902
+ },
2903
+ (args) => definition.handler(args).then((result) => ({ ...result }))
2904
+ );
2905
+ }
2906
+ }
2907
+
2908
+ // src/infrastructure/mcp/user-tools.ts
2909
+ import { z as z11 } from "zod";
2910
+
2911
+ // src/application/users/create-user.use-case.ts
2912
+ var CreateUserUseCase = class {
2913
+ constructor(users) {
2914
+ this.users = users;
2915
+ }
2916
+ users;
2917
+ execute(user) {
2918
+ return this.users.create(user);
2919
+ }
2920
+ };
2921
+
2922
+ // src/application/users/delete-user.use-case.ts
2923
+ var DeleteUserUseCase = class {
2924
+ constructor(users, confirmer) {
2925
+ this.users = users;
2926
+ this.confirmer = confirmer;
2927
+ }
2928
+ users;
2929
+ confirmer;
2930
+ async execute(input) {
2931
+ const user = await this.users.findById(input.id);
2932
+ if (user === null) {
2933
+ return { deleted: false, reason: "User not found" };
2934
+ }
2935
+ if (!user.username.equals(input.username)) {
2936
+ return {
2937
+ deleted: false,
2938
+ reason: "Username does not match the target user"
2939
+ };
2940
+ }
2941
+ const sessions = await this.users.countActiveSessions(input.id);
2942
+ const email = user.email === null ? "" : ` (${user.email.toString()})`;
2943
+ const operation = DestructiveOperation.of(
2944
+ `Delete user ${user.username.toString()}`,
2945
+ `Permanently deletes the account${email} and revokes ${sessions} active session(s). This is irreversible.`
2946
+ );
2947
+ if (!await this.confirmer.confirm(operation)) {
2948
+ return { deleted: false, reason: "Operation not confirmed" };
2949
+ }
2950
+ await this.users.delete(input.id);
2951
+ return { deleted: true };
2952
+ }
2953
+ };
2954
+
2955
+ // src/application/users/get-user.use-case.ts
2956
+ var GetUserUseCase = class {
2957
+ constructor(users) {
2958
+ this.users = users;
2959
+ }
2960
+ users;
2961
+ execute(id) {
2962
+ return this.users.findById(id);
2963
+ }
2964
+ };
2965
+
2966
+ // src/application/users/list-user-sessions.use-case.ts
2967
+ var ListUserSessionsUseCase = class {
2968
+ constructor(users) {
2969
+ this.users = users;
2970
+ }
2971
+ users;
2972
+ execute(id) {
2973
+ return this.users.listSessions(id);
2974
+ }
2975
+ };
2976
+
2977
+ // src/application/users/logout-user.use-case.ts
2978
+ var LogoutUserUseCase = class {
2979
+ constructor(users, confirmer) {
2980
+ this.users = users;
2981
+ this.confirmer = confirmer;
2982
+ }
2983
+ users;
2984
+ confirmer;
2985
+ async execute(input) {
2986
+ const user = await this.users.findById(input.id);
2987
+ if (user === null) {
2988
+ return { loggedOut: false, reason: "User not found" };
2989
+ }
2990
+ const sessions = await this.users.countActiveSessions(input.id);
2991
+ const operation = DestructiveOperation.of(
2992
+ `Log out ${user.username.toString()}`,
2993
+ `Revokes ${String(sessions)} active session(s); the user must sign in again.`
2994
+ );
2995
+ if (!await this.confirmer.confirm(operation)) {
2996
+ return { loggedOut: false, reason: "Operation not confirmed" };
2997
+ }
2998
+ await this.users.logout(input.id);
2999
+ return { loggedOut: true };
3000
+ }
3001
+ };
3002
+
3003
+ // src/application/users/reset-user-password.use-case.ts
3004
+ var ResetUserPasswordUseCase = class {
3005
+ constructor(users, confirmer) {
3006
+ this.users = users;
3007
+ this.confirmer = confirmer;
3008
+ }
3009
+ users;
3010
+ confirmer;
3011
+ async execute(input) {
3012
+ const user = await this.users.findById(input.id);
3013
+ if (user === null) {
3014
+ return { reset: false, reason: "User not found" };
3015
+ }
3016
+ const operation = DestructiveOperation.of(
3017
+ `Reset password for ${user.username.toString()}`,
3018
+ `The current password stops working immediately${input.temporary ? " and the user must set a new one at next login" : ""}.`
3019
+ );
3020
+ if (!await this.confirmer.confirm(operation)) {
3021
+ return { reset: false, reason: "Operation not confirmed" };
3022
+ }
3023
+ await this.users.resetPassword(input.id, input.password, input.temporary);
3024
+ return { reset: true };
3025
+ }
3026
+ };
3027
+
3028
+ // src/application/users/search-users.use-case.ts
3029
+ var SearchUsersUseCase = class {
3030
+ constructor(users) {
3031
+ this.users = users;
3032
+ }
3033
+ users;
3034
+ execute(criteria) {
3035
+ return this.users.search(criteria);
3036
+ }
3037
+ };
3038
+
3039
+ // src/application/users/send-action-email.use-case.ts
3040
+ var SendActionEmailUseCase = class {
3041
+ constructor(users) {
3042
+ this.users = users;
3043
+ }
3044
+ users;
3045
+ execute(input) {
3046
+ return this.users.sendActionsEmail(input.id, input.actions);
3047
+ }
3048
+ };
3049
+
3050
+ // src/application/users/set-user-enabled.use-case.ts
3051
+ var SetUserEnabledUseCase = class {
3052
+ constructor(users) {
3053
+ this.users = users;
3054
+ }
3055
+ users;
3056
+ execute(input) {
3057
+ return this.users.setEnabled(input.id, input.enabled);
3058
+ }
3059
+ };
3060
+
3061
+ // src/application/users/update-user.use-case.ts
3062
+ var UpdateUserUseCase = class {
3063
+ constructor(users) {
3064
+ this.users = users;
3065
+ }
3066
+ users;
3067
+ execute(input) {
3068
+ return this.users.update(input.id, input.changes);
3069
+ }
3070
+ };
3071
+
3072
+ // src/domain/shared/action-email-type.ts
3073
+ var ACTIONS = [
3074
+ "VERIFY_EMAIL",
3075
+ "UPDATE_PASSWORD",
3076
+ "UPDATE_PROFILE",
3077
+ "CONFIGURE_TOTP"
3078
+ ];
3079
+ var ActionEmailType = class _ActionEmailType {
3080
+ constructor(value) {
3081
+ this.value = value;
3082
+ }
3083
+ value;
3084
+ static fromString(value) {
3085
+ const match = ACTIONS.find((action) => action === value);
3086
+ if (match === void 0) {
3087
+ throw new Error(`Unknown action email type: ${value}`);
3088
+ }
3089
+ return new _ActionEmailType(match);
3090
+ }
3091
+ toString() {
3092
+ return this.value;
3093
+ }
3094
+ };
3095
+
3096
+ // src/domain/shared/password.ts
3097
+ var Password = class _Password {
3098
+ constructor(value) {
3099
+ this.value = value;
3100
+ }
3101
+ value;
3102
+ static fromString(value) {
3103
+ return new _Password(ensureNonBlank(value, "Password"));
3104
+ }
3105
+ reveal() {
3106
+ return this.value;
3107
+ }
3108
+ /** Prevents accidental leakage through logging / serialization. */
3109
+ toString() {
3110
+ return "[redacted]";
3111
+ }
3112
+ toJSON() {
3113
+ return "[redacted]";
3114
+ }
3115
+ };
3116
+
3117
+ // src/infrastructure/mcp/user-tools.ts
3118
+ function serializeUser2(user) {
3119
+ return {
3120
+ id: user.id.toString(),
3121
+ username: user.username.toString(),
3122
+ email: user.email?.toString() ?? null,
3123
+ enabled: user.enabled
3124
+ };
3125
+ }
3126
+ function buildCriteria(args) {
3127
+ const criteria = {
3128
+ first: typeof args.first === "number" ? args.first : 0,
3129
+ max: typeof args.max === "number" ? args.max : 20
3130
+ };
3131
+ if (typeof args.email === "string") {
3132
+ criteria.email = Email.fromString(args.email);
3133
+ }
3134
+ if (typeof args.username === "string") {
3135
+ criteria.username = Username.fromString(args.username);
3136
+ }
3137
+ if (typeof args.search === "string") {
3138
+ criteria.search = args.search;
3139
+ }
3140
+ return criteria;
3141
+ }
3142
+ function searchUsersTool(deps) {
3143
+ return {
3144
+ name: "keycloak_user_search",
3145
+ title: "Search users",
3146
+ description: "Search realm users by email, username or free text.",
3147
+ level: "read" /* Read */,
3148
+ inputSchema: {
3149
+ email: z11.string().optional(),
3150
+ username: z11.string().optional(),
3151
+ search: z11.string().optional(),
3152
+ first: z11.number().int().min(0).optional(),
3153
+ max: z11.number().int().min(1).max(500).optional()
3154
+ },
3155
+ annotations: {
3156
+ readOnlyHint: true,
3157
+ destructiveHint: false,
3158
+ idempotentHint: true
3159
+ },
3160
+ async handler(args) {
3161
+ const users = await new SearchUsersUseCase(deps.userRepository).execute(
3162
+ buildCriteria(args)
3163
+ );
3164
+ return textResult(JSON.stringify(users.map(serializeUser2), null, 2));
3165
+ }
3166
+ };
3167
+ }
3168
+ function getUserTool(deps) {
3169
+ return {
3170
+ name: "keycloak_user_get",
3171
+ title: "Get user",
3172
+ description: "Fetch a single user by id.",
3173
+ level: "read" /* Read */,
3174
+ inputSchema: { id: z11.string() },
3175
+ annotations: {
3176
+ readOnlyHint: true,
3177
+ destructiveHint: false,
3178
+ idempotentHint: true
3179
+ },
3180
+ async handler(args) {
3181
+ const user = await new GetUserUseCase(deps.userRepository).execute(
3182
+ UserId.fromString(String(args.id))
3183
+ );
3184
+ return textResult(
3185
+ user === null ? "User not found." : JSON.stringify(serializeUser2(user), null, 2)
3186
+ );
3187
+ }
3188
+ };
3189
+ }
3190
+ function createUserTool(deps) {
3191
+ return {
3192
+ name: "keycloak_user_create",
3193
+ title: "Create user",
3194
+ description: "Create a realm user.",
3195
+ level: "write" /* Write */,
3196
+ inputSchema: {
3197
+ username: z11.string(),
3198
+ email: z11.string().optional(),
3199
+ enabled: z11.boolean().optional(),
3200
+ emailVerified: z11.boolean().optional()
3201
+ },
3202
+ annotations: {
3203
+ readOnlyHint: false,
3204
+ destructiveHint: false,
3205
+ idempotentHint: false
3206
+ },
3207
+ async handler(args) {
3208
+ const user = {
3209
+ username: Username.fromString(String(args.username)),
3210
+ ...typeof args.email === "string" ? { email: Email.fromString(args.email) } : {},
3211
+ enabled: args.enabled !== false,
3212
+ emailVerified: args.emailVerified === true
3213
+ };
3214
+ await new CreateUserUseCase(deps.userRepository).execute(user);
3215
+ return textResult(`User "${user.username.toString()}" created.`);
3216
+ }
3217
+ };
3218
+ }
3219
+ function setUserEnabledTool(deps) {
3220
+ return {
3221
+ name: "keycloak_user_set_enabled",
3222
+ title: "Enable or disable user",
3223
+ description: "Enable or disable a user account.",
3224
+ level: "write" /* Write */,
3225
+ inputSchema: { id: z11.string(), enabled: z11.boolean() },
3226
+ annotations: {
3227
+ readOnlyHint: false,
3228
+ destructiveHint: false,
3229
+ idempotentHint: true
3230
+ },
3231
+ async handler(args) {
3232
+ const enabled = args.enabled === true;
3233
+ await new SetUserEnabledUseCase(deps.userRepository).execute({
3234
+ id: UserId.fromString(String(args.id)),
3235
+ enabled
3236
+ });
3237
+ return textResult(
3238
+ `User ${String(args.id)} ${enabled ? "enabled" : "disabled"}.`
3239
+ );
3240
+ }
3241
+ };
3242
+ }
3243
+ function sendActionEmailTool(deps) {
3244
+ return {
3245
+ name: "keycloak_user_send_action_email",
3246
+ title: "Send action email",
3247
+ description: "Send a required-actions email (e.g. VERIFY_EMAIL, UPDATE_PASSWORD).",
3248
+ level: "write" /* Write */,
3249
+ inputSchema: { id: z11.string(), actions: z11.array(z11.string()) },
3250
+ annotations: {
3251
+ readOnlyHint: false,
3252
+ destructiveHint: false,
3253
+ idempotentHint: false
3254
+ },
3255
+ async handler(args) {
3256
+ const raw = Array.isArray(args.actions) ? args.actions : [];
3257
+ const actions = raw.map(
3258
+ (action) => ActionEmailType.fromString(String(action))
3259
+ );
3260
+ await new SendActionEmailUseCase(deps.userRepository).execute({
3261
+ id: UserId.fromString(String(args.id)),
3262
+ actions
3263
+ });
3264
+ return textResult("Action email sent.");
3265
+ }
3266
+ };
3267
+ }
3268
+ function resetUserPasswordTool(deps) {
3269
+ return {
3270
+ name: "keycloak_user_reset_password",
3271
+ title: "Reset user password",
3272
+ description: "Set a new password for a user. Requires confirmation.",
3273
+ level: "destructive" /* Destructive */,
3274
+ inputSchema: {
3275
+ id: z11.string(),
3276
+ password: z11.string(),
3277
+ temporary: z11.boolean().optional(),
3278
+ confirm: z11.boolean().optional()
3279
+ },
3280
+ annotations: {
3281
+ readOnlyHint: false,
3282
+ destructiveHint: true,
3283
+ idempotentHint: false
3284
+ },
3285
+ async handler(args) {
3286
+ const confirmer = deps.confirmers.create(args.confirm === true);
3287
+ const result = await new ResetUserPasswordUseCase(
3288
+ deps.userRepository,
3289
+ confirmer
3290
+ ).execute({
3291
+ id: UserId.fromString(String(args.id)),
3292
+ password: Password.fromString(String(args.password)),
3293
+ temporary: args.temporary === true
3294
+ });
3295
+ return textResult(
3296
+ result.reset ? "Password reset." : `Not reset: ${result.reason ?? "unknown reason"}`
3297
+ );
3298
+ }
3299
+ };
3300
+ }
3301
+ function logoutUserTool(deps) {
3302
+ return {
3303
+ name: "keycloak_user_logout",
3304
+ title: "Log out user",
3305
+ description: "Revoke all of a user's sessions. Requires confirmation.",
3306
+ level: "destructive" /* Destructive */,
3307
+ inputSchema: { id: z11.string(), confirm: z11.boolean().optional() },
3308
+ annotations: {
3309
+ readOnlyHint: false,
3310
+ destructiveHint: true,
3311
+ idempotentHint: false
3312
+ },
3313
+ async handler(args) {
3314
+ const confirmer = deps.confirmers.create(args.confirm === true);
3315
+ const result = await new LogoutUserUseCase(
3316
+ deps.userRepository,
3317
+ confirmer
3318
+ ).execute({ id: UserId.fromString(String(args.id)) });
3319
+ return textResult(
3320
+ result.loggedOut ? "User logged out." : `Not logged out: ${result.reason ?? "unknown reason"}`
3321
+ );
3322
+ }
3323
+ };
3324
+ }
3325
+ function deleteUserTool(deps) {
3326
+ return {
3327
+ name: "keycloak_user_delete",
3328
+ title: "Delete user",
3329
+ description: "Permanently delete a user. Requires confirmation; the username must match the target id.",
3330
+ level: "destructive" /* Destructive */,
3331
+ inputSchema: {
3332
+ id: z11.string(),
3333
+ username: z11.string(),
3334
+ confirm: z11.boolean().optional()
3335
+ },
3336
+ annotations: {
3337
+ readOnlyHint: false,
3338
+ destructiveHint: true,
3339
+ idempotentHint: false
3340
+ },
3341
+ async handler(args) {
3342
+ const confirmer = deps.confirmers.create(args.confirm === true);
3343
+ const result = await new DeleteUserUseCase(
3344
+ deps.userRepository,
3345
+ confirmer
3346
+ ).execute({
3347
+ id: UserId.fromString(String(args.id)),
3348
+ username: Username.fromString(String(args.username))
3349
+ });
3350
+ if (!result.deleted) {
3351
+ return textResult(`Not deleted: ${result.reason ?? "unknown reason"}`);
3352
+ }
3353
+ return textResult(`User "${String(args.username)}" deleted.`);
3354
+ }
3355
+ };
3356
+ }
3357
+ function updateUserTool(deps) {
3358
+ return {
3359
+ name: "keycloak_user_update",
3360
+ title: "Update user",
3361
+ description: "Update a user's email, name or enabled flag.",
3362
+ level: "write" /* Write */,
3363
+ inputSchema: {
3364
+ id: z11.string(),
3365
+ email: z11.string().optional(),
3366
+ firstName: z11.string().optional(),
3367
+ lastName: z11.string().optional(),
3368
+ enabled: z11.boolean().optional()
3369
+ },
3370
+ annotations: {
3371
+ readOnlyHint: false,
3372
+ destructiveHint: false,
3373
+ idempotentHint: true
3374
+ },
3375
+ async handler(args) {
3376
+ const changes = {};
3377
+ if (typeof args.email === "string") {
3378
+ changes.email = Email.fromString(args.email);
3379
+ }
3380
+ if (typeof args.firstName === "string") {
3381
+ changes.firstName = args.firstName;
3382
+ }
3383
+ if (typeof args.lastName === "string") {
3384
+ changes.lastName = args.lastName;
3385
+ }
3386
+ if (typeof args.enabled === "boolean") {
3387
+ changes.enabled = args.enabled;
3388
+ }
3389
+ await new UpdateUserUseCase(deps.userRepository).execute({
3390
+ id: UserId.fromString(String(args.id)),
3391
+ changes
3392
+ });
3393
+ return textResult(`User ${String(args.id)} updated.`);
3394
+ }
3395
+ };
3396
+ }
3397
+ function listUserSessionsTool(deps) {
3398
+ return {
3399
+ name: "keycloak_user_sessions_list",
3400
+ title: "List user sessions",
3401
+ description: "List a user's active sessions.",
3402
+ level: "read" /* Read */,
3403
+ inputSchema: { id: z11.string() },
3404
+ annotations: {
3405
+ readOnlyHint: true,
3406
+ destructiveHint: false,
3407
+ idempotentHint: true
3408
+ },
3409
+ async handler(args) {
3410
+ const sessions = await new ListUserSessionsUseCase(
3411
+ deps.userRepository
3412
+ ).execute(UserId.fromString(String(args.id)));
3413
+ return textResult(JSON.stringify(sessions, null, 2));
3414
+ }
3415
+ };
3416
+ }
3417
+ function buildUserTools(deps) {
3418
+ return [
3419
+ searchUsersTool(deps),
3420
+ getUserTool(deps),
3421
+ listUserSessionsTool(deps),
3422
+ createUserTool(deps),
3423
+ updateUserTool(deps),
3424
+ setUserEnabledTool(deps),
3425
+ sendActionEmailTool(deps),
3426
+ resetUserPasswordTool(deps),
3427
+ logoutUserTool(deps),
3428
+ deleteUserTool(deps)
3429
+ ];
3430
+ }
3431
+
3432
+ // src/server.ts
3433
+ var httpFetch = (url, init) => fetch(url, init);
3434
+ function createTokenProvider(config) {
3435
+ if (config.auth.mode === "service_account") {
3436
+ return createClientCredentialsProvider(
3437
+ {
3438
+ baseUrl: config.baseUrl,
3439
+ realm: config.realm,
3440
+ clientId: config.auth.clientId,
3441
+ clientSecret: config.auth.clientSecret
3442
+ },
3443
+ httpFetch,
3444
+ systemClock
3445
+ );
3446
+ }
3447
+ return createPasswordProvider(
3448
+ {
3449
+ baseUrl: config.baseUrl,
3450
+ realm: config.auth.adminRealm,
3451
+ username: config.auth.username,
3452
+ password: config.auth.password
3453
+ },
3454
+ httpFetch,
3455
+ systemClock
3456
+ );
3457
+ }
3458
+ function createServer(config) {
3459
+ RealmAccessPolicy.of(
3460
+ config.allowedRealms.map((realm) => RealmName.fromString(realm))
3461
+ ).assertAllowed(RealmName.fromString(config.realm));
3462
+ const server = new McpServer({
3463
+ name: "mcp-keycloak-admin",
3464
+ version: "0.0.0"
3465
+ });
3466
+ const tokens = createTokenProvider(config);
3467
+ const client = new KeycloakAdminClient(
3468
+ { baseUrl: config.baseUrl, realm: config.realm },
3469
+ tokens,
3470
+ httpFetch
3471
+ );
3472
+ const userRepository = new KeycloakUserRepository(client);
3473
+ const roleRepository = new KeycloakRoleRepository(client);
3474
+ const clientRepository = new KeycloakClientRepository(client);
3475
+ const clientScopeRepository = new KeycloakClientScopeRepository(client);
3476
+ const groupRepository = new KeycloakGroupRepository(client);
3477
+ const eventLog = new KeycloakEventLog(client);
3478
+ const realmInfo = new KeycloakRealmInfo(client);
3479
+ const identityProviderRepository = new KeycloakIdentityProviderRepository(
3480
+ client
3481
+ );
3482
+ const federationRepository = new KeycloakFederationRepository(client);
3483
+ const authenticationRepository = new KeycloakAuthenticationRepository(client);
3484
+ const authorizationRepository = new KeycloakAuthorizationRepository(client);
3485
+ const confirmers = new McpConfirmerFactory(server);
3486
+ const tools = filterTools(
3487
+ [
3488
+ ...buildUserTools({ userRepository, confirmers }),
3489
+ ...buildRoleTools({ roleRepository, confirmers }),
3490
+ ...buildClientTools({ clientRepository, confirmers }),
3491
+ ...buildClientScopeTools({
3492
+ clientRepository,
3493
+ clientScopeRepository,
3494
+ confirmers
3495
+ }),
3496
+ ...buildGroupTools({ groupRepository, roleRepository, confirmers }),
3497
+ ...buildIdpTools({ identityProviderRepository, confirmers }),
3498
+ ...buildFederationTools({ federationRepository }),
3499
+ ...buildAuthTools({ authenticationRepository }),
3500
+ ...buildAuthzTools({ clientRepository, authorizationRepository }),
3501
+ ...buildEventRealmTools({ eventLog, realmInfo })
3502
+ ],
3503
+ ToolAccessPolicy.of(config.readOnly)
3504
+ );
3505
+ registerTools(server, tools);
3506
+ return server;
3507
+ }
3508
+
3509
+ // src/index.ts
3510
+ async function main() {
3511
+ const config = loadConfig(process.env);
3512
+ const server = createServer(config);
3513
+ await server.connect(new StdioServerTransport());
3514
+ }
3515
+ main().catch((error) => {
3516
+ console.error(error);
3517
+ process.exitCode = 1;
3518
+ });
3519
+ //# sourceMappingURL=index.js.map