keycloak-model-context-protocol 0.0.2 → 1.0.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/LICENSE +21 -0
- package/README.md +188 -101
- package/dist/config.js +178 -0
- package/dist/config.js.map +1 -0
- package/dist/guards.js +47 -0
- package/dist/guards.js.map +1 -0
- package/dist/index.js +36 -174
- package/dist/index.js.map +1 -0
- package/dist/keycloak/errors.js +86 -0
- package/dist/keycloak/errors.js.map +1 -0
- package/dist/keycloak/gateway.js +131 -0
- package/dist/keycloak/gateway.js.map +1 -0
- package/dist/logging.js +49 -0
- package/dist/logging.js.map +1 -0
- package/dist/prompts/audit-realm-security.js +26 -0
- package/dist/prompts/audit-realm-security.js.map +1 -0
- package/dist/prompts/index.js +12 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/investigate-failed-logins.js +34 -0
- package/dist/prompts/investigate-failed-logins.js.map +1 -0
- package/dist/prompts/offboard-employee.js +31 -0
- package/dist/prompts/offboard-employee.js.map +1 -0
- package/dist/prompts/onboard-employee.js +50 -0
- package/dist/prompts/onboard-employee.js.map +1 -0
- package/dist/prompts/shared.js +46 -0
- package/dist/prompts/shared.js.map +1 -0
- package/dist/resources/index.js +8 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/realms.js +96 -0
- package/dist/resources/realms.js.map +1 -0
- package/dist/resources/shared.js +44 -0
- package/dist/resources/shared.js.map +1 -0
- package/dist/resources/users.js +26 -0
- package/dist/resources/users.js.map +1 -0
- package/dist/server.js +16 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/clients.js +137 -0
- package/dist/tools/clients.js.map +1 -0
- package/dist/tools/events.js +161 -0
- package/dist/tools/events.js.map +1 -0
- package/dist/tools/groups.js +135 -0
- package/dist/tools/groups.js.map +1 -0
- package/dist/tools/index.js +31 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/realms.js +99 -0
- package/dist/tools/realms.js.map +1 -0
- package/dist/tools/roles.js +270 -0
- package/dist/tools/roles.js.map +1 -0
- package/dist/tools/security.js +58 -0
- package/dist/tools/security.js.map +1 -0
- package/dist/tools/sessions.js +68 -0
- package/dist/tools/sessions.js.map +1 -0
- package/dist/tools/shared.js +144 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/users.js +272 -0
- package/dist/tools/users.js.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +46 -11
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineTool, pageOf, paginationShape, realmField, toPage } from "./shared.js";
|
|
3
|
+
const dateField = (label) => z
|
|
4
|
+
.string()
|
|
5
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, "Use YYYY-MM-DD")
|
|
6
|
+
.optional()
|
|
7
|
+
.describe(`${label} (YYYY-MM-DD, inclusive).`);
|
|
8
|
+
const SENSITIVE_KEY = /token|secret|password|credential|^code$/i;
|
|
9
|
+
/** Keeps string-valued detail entries, dropping anything that looks like a token or secret. */
|
|
10
|
+
function safeDetails(details) {
|
|
11
|
+
const out = {};
|
|
12
|
+
for (const [key, value] of Object.entries(details ?? {})) {
|
|
13
|
+
if (SENSITIVE_KEY.test(key))
|
|
14
|
+
continue;
|
|
15
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
16
|
+
out[key] = String(value);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
const iso = (ms) => (ms === undefined ? undefined : new Date(ms).toISOString());
|
|
22
|
+
const DISABLED_HINT = " The list is empty; events may be disabled for this realm (check the realm's event settings).";
|
|
23
|
+
const loginEvent = z.object({
|
|
24
|
+
time: z.string().optional().describe("Event time (ISO 8601)."),
|
|
25
|
+
type: z.string().optional().describe("Event type, e.g. LOGIN or LOGIN_ERROR."),
|
|
26
|
+
userId: z.string().optional().describe("User ID, if known."),
|
|
27
|
+
clientId: z.string().optional().describe("Client involved, if known."),
|
|
28
|
+
ipAddress: z.string().optional().describe("Source IP address."),
|
|
29
|
+
error: z.string().optional().describe("Error code for failed events."),
|
|
30
|
+
details: z.record(z.string(), z.string()).describe("Event details; tokens are removed."),
|
|
31
|
+
});
|
|
32
|
+
const eventList = defineTool({
|
|
33
|
+
name: "event_list",
|
|
34
|
+
title: "List login events",
|
|
35
|
+
description: "List user/login events of a realm (logins, failed logins, logouts, password changes) with filters and pagination. Requires event storage to be enabled in the realm.",
|
|
36
|
+
kind: "read",
|
|
37
|
+
input: z.object({
|
|
38
|
+
realm: realmField,
|
|
39
|
+
type: z
|
|
40
|
+
.array(z.string().min(1))
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Event types to include, e.g. ['LOGIN', 'LOGIN_ERROR']."),
|
|
43
|
+
user: z.string().optional().describe("Filter by user ID."),
|
|
44
|
+
client: z.string().optional().describe("Filter by clientId."),
|
|
45
|
+
ipAddress: z.string().optional().describe("Filter by source IP address."),
|
|
46
|
+
dateFrom: dateField("Earliest event date"),
|
|
47
|
+
dateTo: dateField("Latest event date"),
|
|
48
|
+
...paginationShape,
|
|
49
|
+
}),
|
|
50
|
+
output: pageOf(loginEvent).extend({ realm: z.string().describe("Realm that was queried.") }),
|
|
51
|
+
async handler({ realm, type, user, client, ipAddress, dateFrom, dateTo, first, max }, { gateway }) {
|
|
52
|
+
const rows = await gateway.run((kc) => kc.realms.findEvents({
|
|
53
|
+
realm,
|
|
54
|
+
first,
|
|
55
|
+
max: max + 1,
|
|
56
|
+
// Event types are validated by Keycloak; the client's union type is too narrow for free input.
|
|
57
|
+
...(type !== undefined && { type: type }),
|
|
58
|
+
...(user !== undefined && { user }),
|
|
59
|
+
...(client !== undefined && { client }),
|
|
60
|
+
...(ipAddress !== undefined && { ipAddress }),
|
|
61
|
+
...(dateFrom !== undefined && { dateFrom }),
|
|
62
|
+
...(dateTo !== undefined && { dateTo }),
|
|
63
|
+
}));
|
|
64
|
+
const mapped = rows.map((e) => ({
|
|
65
|
+
...(e.time !== undefined && { time: iso(e.time) }),
|
|
66
|
+
...(e.type !== undefined && { type: e.type }),
|
|
67
|
+
...(e.userId !== undefined && { userId: e.userId }),
|
|
68
|
+
...(e.clientId !== undefined && { clientId: e.clientId }),
|
|
69
|
+
...(e.ipAddress !== undefined && { ipAddress: e.ipAddress }),
|
|
70
|
+
...(e.error !== undefined && { error: e.error }),
|
|
71
|
+
details: safeDetails(e.details),
|
|
72
|
+
}));
|
|
73
|
+
const page = toPage(mapped, first, max);
|
|
74
|
+
const empty = page.items.length === 0 && first === 0;
|
|
75
|
+
return {
|
|
76
|
+
data: { ...page, realm },
|
|
77
|
+
summary: `Found ${page.items.length} event(s) in realm '${realm}'${page.hasMore ? " (more available)" : ""}.${empty ? DISABLED_HINT : ""}`,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
const adminEvent = z.object({
|
|
82
|
+
time: z.string().optional().describe("Event time (ISO 8601)."),
|
|
83
|
+
operationType: z.string().optional().describe("CREATE, UPDATE, DELETE or ACTION."),
|
|
84
|
+
resourceType: z.string().optional().describe("Type of the changed resource, e.g. USER."),
|
|
85
|
+
resourcePath: z.string().optional().describe("Path of the changed resource."),
|
|
86
|
+
authDetails: z
|
|
87
|
+
.object({
|
|
88
|
+
userId: z.string().optional().describe("Admin user who made the change."),
|
|
89
|
+
realmId: z.string().optional().describe("Realm of the admin user."),
|
|
90
|
+
clientId: z.string().optional().describe("Client used for the change."),
|
|
91
|
+
ipAddress: z.string().optional().describe("Source IP address."),
|
|
92
|
+
})
|
|
93
|
+
.optional()
|
|
94
|
+
.describe("Who performed the operation."),
|
|
95
|
+
error: z.string().optional().describe("Error code if the operation failed."),
|
|
96
|
+
});
|
|
97
|
+
const adminEventList = defineTool({
|
|
98
|
+
name: "admin_event_list",
|
|
99
|
+
title: "List admin events",
|
|
100
|
+
description: "List admin (audit) events of a realm: who changed which resource and when. The change payload is deliberately omitted. Requires admin events to be enabled in the realm.",
|
|
101
|
+
kind: "read",
|
|
102
|
+
input: z.object({
|
|
103
|
+
realm: realmField,
|
|
104
|
+
operationTypes: z
|
|
105
|
+
.array(z.string().min(1))
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Operation types to include: CREATE, UPDATE, DELETE, ACTION."),
|
|
108
|
+
resourceTypes: z
|
|
109
|
+
.array(z.string().min(1))
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Resource types to include, e.g. ['USER', 'CLIENT']."),
|
|
112
|
+
resourcePath: z.string().optional().describe("Resource path filter; '*' wildcards allowed."),
|
|
113
|
+
authUser: z.string().optional().describe("Filter by ID of the acting admin user."),
|
|
114
|
+
dateFrom: dateField("Earliest event date"),
|
|
115
|
+
dateTo: dateField("Latest event date"),
|
|
116
|
+
...paginationShape,
|
|
117
|
+
}),
|
|
118
|
+
output: pageOf(adminEvent).extend({ realm: z.string().describe("Realm that was queried.") }),
|
|
119
|
+
async handler({ realm, operationTypes, resourceTypes, resourcePath, authUser, dateFrom, dateTo, first, max }, { gateway }) {
|
|
120
|
+
const rows = await gateway.run((kc) => kc.realms.findAdminEvents({
|
|
121
|
+
realm,
|
|
122
|
+
first,
|
|
123
|
+
max: max + 1,
|
|
124
|
+
// The client serializes arrays as repeated params and Dates via toString(); the typings
|
|
125
|
+
// say string/Date, but Keycloak wants repeated params and YYYY-MM-DD strings.
|
|
126
|
+
...(operationTypes !== undefined && {
|
|
127
|
+
operationTypes: operationTypes,
|
|
128
|
+
}),
|
|
129
|
+
...(resourceTypes !== undefined && { resourceTypes: resourceTypes }),
|
|
130
|
+
...(resourcePath !== undefined && { resourcePath }),
|
|
131
|
+
...(authUser !== undefined && { authUser }),
|
|
132
|
+
...(dateFrom !== undefined && { dateFrom: dateFrom }),
|
|
133
|
+
...(dateTo !== undefined && { dateTo: dateTo }),
|
|
134
|
+
}));
|
|
135
|
+
const mapped = rows.map((e) => {
|
|
136
|
+
const a = e.authDetails;
|
|
137
|
+
const authDetails = a && {
|
|
138
|
+
...(a.userId !== undefined && { userId: a.userId }),
|
|
139
|
+
...(a.realmId !== undefined && { realmId: a.realmId }),
|
|
140
|
+
...(a.clientId !== undefined && { clientId: a.clientId }),
|
|
141
|
+
...(a.ipAddress !== undefined && { ipAddress: a.ipAddress }),
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
...(e.time !== undefined && { time: iso(e.time) }),
|
|
145
|
+
...(e.operationType !== undefined && { operationType: e.operationType }),
|
|
146
|
+
...(e.resourceType !== undefined && { resourceType: e.resourceType }),
|
|
147
|
+
...(e.resourcePath !== undefined && { resourcePath: e.resourcePath }),
|
|
148
|
+
...(authDetails && { authDetails }),
|
|
149
|
+
...(e.error !== undefined && { error: e.error }),
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
const page = toPage(mapped, first, max);
|
|
153
|
+
const empty = page.items.length === 0 && first === 0;
|
|
154
|
+
return {
|
|
155
|
+
data: { ...page, realm },
|
|
156
|
+
summary: `Found ${page.items.length} admin event(s) in realm '${realm}'${page.hasMore ? " (more available)" : ""}.${empty ? DISABLED_HINT : ""}`,
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
export const eventTools = [eventList, adminEventList];
|
|
161
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/tools/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ToolError } from "../keycloak/errors.js";
|
|
3
|
+
import { defineTool, pageOf, paginationShape, realmField, toPage } from "./shared.js";
|
|
4
|
+
import { toUserSummary } from "./users.js";
|
|
5
|
+
const groupSummary = z.object({
|
|
6
|
+
id: z.string().describe("Group ID (UUID); use it as `groupId` in other tools."),
|
|
7
|
+
name: z.string().describe("Group name."),
|
|
8
|
+
path: z.string().optional().describe("Full group path, e.g. /parent/child."),
|
|
9
|
+
subGroupCount: z.number().optional().describe("Number of direct subgroups."),
|
|
10
|
+
});
|
|
11
|
+
const groupList = defineTool({
|
|
12
|
+
name: "group_list",
|
|
13
|
+
title: "List groups",
|
|
14
|
+
description: "List groups of a realm, optionally filtered by a search string, with pagination. Returns id, name, path and number of subgroups.",
|
|
15
|
+
kind: "read",
|
|
16
|
+
input: z.object({
|
|
17
|
+
realm: realmField,
|
|
18
|
+
search: z.string().optional().describe("Only groups whose name contains this text."),
|
|
19
|
+
...paginationShape,
|
|
20
|
+
}),
|
|
21
|
+
output: pageOf(groupSummary).extend({ realm: z.string().describe("Realm that was queried.") }),
|
|
22
|
+
async handler({ realm, search, first, max }, { gateway }) {
|
|
23
|
+
const rows = await gateway.run((kc) => kc.groups.find({
|
|
24
|
+
realm,
|
|
25
|
+
first,
|
|
26
|
+
max: max + 1,
|
|
27
|
+
briefRepresentation: true,
|
|
28
|
+
...(search !== undefined && { search }),
|
|
29
|
+
}));
|
|
30
|
+
const items = rows.map((g) => ({
|
|
31
|
+
id: g.id ?? "",
|
|
32
|
+
name: g.name ?? "",
|
|
33
|
+
...(g.path !== undefined && { path: g.path }),
|
|
34
|
+
...(g.subGroupCount !== undefined && { subGroupCount: g.subGroupCount }),
|
|
35
|
+
}));
|
|
36
|
+
const page = toPage(items, first, max);
|
|
37
|
+
return {
|
|
38
|
+
data: { ...page, realm },
|
|
39
|
+
summary: `Found ${page.items.length} group(s) in realm '${realm}' (first=${first}, max=${max}${page.hasMore ? ", more available" : ""}).`,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const groupMembersList = defineTool({
|
|
44
|
+
name: "group_members_list",
|
|
45
|
+
title: "List group members",
|
|
46
|
+
description: "List the members of a group with a safe subset of user fields, with pagination.",
|
|
47
|
+
kind: "read",
|
|
48
|
+
input: z.object({
|
|
49
|
+
realm: realmField,
|
|
50
|
+
groupId: z.string().min(1).describe("ID (UUID) of the group, e.g. from group_list."),
|
|
51
|
+
...paginationShape,
|
|
52
|
+
}),
|
|
53
|
+
output: pageOf(z.object({
|
|
54
|
+
id: z.string().describe("User ID (UUID)."),
|
|
55
|
+
username: z.string().describe("Login name."),
|
|
56
|
+
email: z.string().optional().describe("E-mail address, if set."),
|
|
57
|
+
firstName: z.string().optional().describe("First name, if set."),
|
|
58
|
+
lastName: z.string().optional().describe("Last name, if set."),
|
|
59
|
+
enabled: z.boolean().optional().describe("Whether the user can log in."),
|
|
60
|
+
emailVerified: z.boolean().optional().describe("Whether the e-mail is verified."),
|
|
61
|
+
})).extend({
|
|
62
|
+
realm: z.string().describe("Realm that was queried."),
|
|
63
|
+
groupId: z.string().describe("Group that was queried."),
|
|
64
|
+
}),
|
|
65
|
+
async handler({ realm, groupId, first, max }, { gateway }) {
|
|
66
|
+
const rows = await gateway.run((kc) => kc.groups.listMembers({ realm, id: groupId, first, max: max + 1, briefRepresentation: true }));
|
|
67
|
+
const page = toPage(rows.map(toUserSummary), first, max);
|
|
68
|
+
return {
|
|
69
|
+
data: { ...page, realm, groupId },
|
|
70
|
+
summary: `Found ${page.items.length} member(s) in group ${groupId} (first=${first}, max=${max}${page.hasMore ? ", more available" : ""}).`,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const membershipOutput = z.object({
|
|
75
|
+
userId: z.string().describe("ID of the user."),
|
|
76
|
+
groupId: z.string().describe("ID of the group."),
|
|
77
|
+
realm: z.string().describe("Realm of the user and group."),
|
|
78
|
+
});
|
|
79
|
+
const userGroupAdd = defineTool({
|
|
80
|
+
name: "user_group_add",
|
|
81
|
+
title: "Add user to group",
|
|
82
|
+
description: "Add a user to a group. Adding a user who is already a member has no further effect.",
|
|
83
|
+
kind: "write",
|
|
84
|
+
idempotent: true,
|
|
85
|
+
input: z.object({
|
|
86
|
+
realm: realmField,
|
|
87
|
+
userId: z.string().min(1).describe("ID (UUID) of the user."),
|
|
88
|
+
groupId: z.string().min(1).describe("ID (UUID) of the group."),
|
|
89
|
+
}),
|
|
90
|
+
output: membershipOutput.extend({
|
|
91
|
+
added: z.boolean().describe("True once the user is a member."),
|
|
92
|
+
}),
|
|
93
|
+
async handler({ realm, userId, groupId }, { gateway }) {
|
|
94
|
+
await gateway.run((kc) => kc.users.addToGroup({ realm, id: userId, groupId }));
|
|
95
|
+
return {
|
|
96
|
+
data: { userId, groupId, realm, added: true },
|
|
97
|
+
summary: `User ${userId} is now a member of group ${groupId} in realm '${realm}'.`,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
const userGroupRemove = defineTool({
|
|
102
|
+
name: "user_group_remove",
|
|
103
|
+
title: "Remove user from group",
|
|
104
|
+
description: "Remove a user from a group. This may revoke the roles the user inherits from the group. Requires confirm: true (ask the user first).",
|
|
105
|
+
kind: "destructive",
|
|
106
|
+
idempotent: true,
|
|
107
|
+
input: z.object({
|
|
108
|
+
realm: realmField,
|
|
109
|
+
userId: z.string().min(1).describe("ID (UUID) of the user."),
|
|
110
|
+
groupId: z.string().min(1).describe("ID (UUID) of the group."),
|
|
111
|
+
}),
|
|
112
|
+
output: membershipOutput.extend({
|
|
113
|
+
removed: z.boolean().describe("True once the user is no member."),
|
|
114
|
+
}),
|
|
115
|
+
async preview({ realm, userId, groupId }, { gateway }) {
|
|
116
|
+
const [user, group] = await gateway.run((kc) => Promise.all([
|
|
117
|
+
kc.users.findOne({ realm, id: userId }),
|
|
118
|
+
kc.groups.findOne({ realm, id: groupId }),
|
|
119
|
+
]));
|
|
120
|
+
if (!user)
|
|
121
|
+
throw new ToolError("not_found", `User ${userId} does not exist in realm '${realm}'.`);
|
|
122
|
+
if (!group)
|
|
123
|
+
throw new ToolError("not_found", `Group ${groupId} does not exist in realm '${realm}'.`);
|
|
124
|
+
return `remove user '${user.username ?? userId}' from group '${group.path ?? group.name ?? groupId}' in realm '${realm}'.`;
|
|
125
|
+
},
|
|
126
|
+
async handler({ realm, userId, groupId }, { gateway }) {
|
|
127
|
+
await gateway.run((kc) => kc.users.delFromGroup({ realm, id: userId, groupId }));
|
|
128
|
+
return {
|
|
129
|
+
data: { userId, groupId, realm, removed: true },
|
|
130
|
+
summary: `User ${userId} is no longer a member of group ${groupId} in realm '${realm}'.`,
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
export const groupTools = [groupList, groupMembersList, userGroupAdd, userGroupRemove];
|
|
135
|
+
//# sourceMappingURL=groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/tools/groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { clientTools } from "./clients.js";
|
|
2
|
+
import { eventTools } from "./events.js";
|
|
3
|
+
import { groupTools } from "./groups.js";
|
|
4
|
+
import { realmTools } from "./realms.js";
|
|
5
|
+
import { roleTools } from "./roles.js";
|
|
6
|
+
import { securityTools } from "./security.js";
|
|
7
|
+
import { sessionTools } from "./sessions.js";
|
|
8
|
+
import { userTools } from "./users.js";
|
|
9
|
+
/** Every toolset and its tools. A new tool only needs to be added to its domain file. */
|
|
10
|
+
export const TOOLSETS = {
|
|
11
|
+
core: [...realmTools, ...userTools],
|
|
12
|
+
groups: groupTools,
|
|
13
|
+
roles: roleTools,
|
|
14
|
+
clients: clientTools,
|
|
15
|
+
sessions: sessionTools,
|
|
16
|
+
events: eventTools,
|
|
17
|
+
security: securityTools,
|
|
18
|
+
};
|
|
19
|
+
/** Tools enabled by config: selected toolsets, minus write tools in read-only mode. */
|
|
20
|
+
export function selectTools(config) {
|
|
21
|
+
return Object.entries(TOOLSETS)
|
|
22
|
+
.filter(([name]) => config.toolsets.has(name))
|
|
23
|
+
.flatMap(([, tools]) => tools)
|
|
24
|
+
.filter((tool) => !config.readOnly || tool.kind === "read");
|
|
25
|
+
}
|
|
26
|
+
export function registerTools(server, ctx) {
|
|
27
|
+
for (const tool of selectTools(ctx.config)) {
|
|
28
|
+
tool.register(server, ctx);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,yFAAyF;AACzF,MAAM,CAAC,MAAM,QAAQ,GAAmD;IACtE,IAAI,EAAE,CAAC,GAAG,UAAU,EAAE,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;IACtB,MAAM,EAAE,UAAU;IAClB,QAAQ,EAAE,aAAa;CACxB,CAAC;AAEF,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,MAA6C;IACvE,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;SAC5B,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAmB,CAAC,CAAC;SAC5D,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC;SAC7B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,GAAgB;IAC/D,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { isRealmAllowed } from "../guards.js";
|
|
3
|
+
import { ToolError } from "../keycloak/errors.js";
|
|
4
|
+
import { defineTool, pageOf, paginationShape, realmField, toPage } from "./shared.js";
|
|
5
|
+
const realmSummary = z.object({
|
|
6
|
+
realm: z.string().describe("Realm name (use this as the `realm` argument of other tools)."),
|
|
7
|
+
displayName: z.string().optional().describe("Human-readable realm name."),
|
|
8
|
+
enabled: z.boolean().optional().describe("Whether the realm is enabled."),
|
|
9
|
+
});
|
|
10
|
+
const realmList = defineTool({
|
|
11
|
+
name: "realm_list",
|
|
12
|
+
title: "List realms",
|
|
13
|
+
description: "List the Keycloak realms visible to the configured admin account (restricted to KEYCLOAK_MCP_REALMS if set). Returns names only, not realm settings.",
|
|
14
|
+
kind: "read",
|
|
15
|
+
input: z.object(paginationShape),
|
|
16
|
+
output: pageOf(realmSummary),
|
|
17
|
+
async handler({ first, max }, { gateway, config }) {
|
|
18
|
+
const realms = await gateway.run((kc) => kc.realms.find({ briefRepresentation: true }));
|
|
19
|
+
const visible = realms
|
|
20
|
+
.filter((r) => r.realm !== undefined && isRealmAllowed(config, r.realm))
|
|
21
|
+
.sort((a, b) => (a.realm ?? "").localeCompare(b.realm ?? ""))
|
|
22
|
+
.map((r) => ({
|
|
23
|
+
realm: r.realm,
|
|
24
|
+
...(r.displayName ? { displayName: r.displayName } : {}),
|
|
25
|
+
...(r.enabled !== undefined ? { enabled: r.enabled } : {}),
|
|
26
|
+
}));
|
|
27
|
+
const page = toPage(visible.slice(first, first + max + 1), first, max);
|
|
28
|
+
return { data: page, summary: `Returned ${page.items.length} of ${visible.length} realm(s).` };
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const realmGet = defineTool({
|
|
32
|
+
name: "realm_get",
|
|
33
|
+
title: "Get realm settings",
|
|
34
|
+
description: "Get a safe subset of a realm's settings (login, session, brute-force, password policy and event flags). SMTP credentials, keys and other secrets are never returned.",
|
|
35
|
+
kind: "read",
|
|
36
|
+
input: z.object({ realm: realmField }),
|
|
37
|
+
output: z.object({
|
|
38
|
+
realm: z.string().describe("Realm name."),
|
|
39
|
+
displayName: z.string().optional().describe("Human-readable realm name."),
|
|
40
|
+
enabled: z.boolean().optional().describe("Whether the realm is enabled."),
|
|
41
|
+
sslRequired: z.string().optional().describe("SSL requirement: all, external or none."),
|
|
42
|
+
registrationAllowed: z.boolean().optional().describe("Whether self-registration is allowed."),
|
|
43
|
+
loginWithEmailAllowed: z.boolean().optional().describe("Whether users may log in with e-mail."),
|
|
44
|
+
duplicateEmailsAllowed: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Whether duplicate e-mails are allowed."),
|
|
48
|
+
resetPasswordAllowed: z.boolean().optional().describe("Whether 'forgot password' is enabled."),
|
|
49
|
+
verifyEmail: z.boolean().optional().describe("Whether e-mail verification is required."),
|
|
50
|
+
bruteForceProtected: z.boolean().optional().describe("Whether brute-force detection is on."),
|
|
51
|
+
passwordPolicy: z.string().optional().describe("Password policy expression."),
|
|
52
|
+
otpPolicyType: z.string().optional().describe("OTP type: totp or hotp."),
|
|
53
|
+
accessTokenLifespan: z.number().optional().describe("Access token lifespan in seconds."),
|
|
54
|
+
ssoSessionIdleTimeout: z.number().optional().describe("SSO session idle timeout in seconds."),
|
|
55
|
+
ssoSessionMaxLifespan: z.number().optional().describe("SSO session max lifespan in seconds."),
|
|
56
|
+
eventsEnabled: z.boolean().optional().describe("Whether login events are stored."),
|
|
57
|
+
adminEventsEnabled: z.boolean().optional().describe("Whether admin events are stored."),
|
|
58
|
+
}),
|
|
59
|
+
async handler({ realm }, { gateway }) {
|
|
60
|
+
const rep = await gateway.run((kc) => kc.realms.findOne({ realm }));
|
|
61
|
+
if (!rep)
|
|
62
|
+
throw new ToolError("not_found", `Realm '${realm}' does not exist.`);
|
|
63
|
+
const data = toRealmSettings(rep, realm);
|
|
64
|
+
return { data, summary: `Returned settings of realm '${realm}'.` };
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
/** Field allowlist for realm settings; SMTP credentials, keys and secrets never pass. */
|
|
68
|
+
export const REALM_SETTING_FIELDS = [
|
|
69
|
+
"displayName",
|
|
70
|
+
"enabled",
|
|
71
|
+
"sslRequired",
|
|
72
|
+
"registrationAllowed",
|
|
73
|
+
"loginWithEmailAllowed",
|
|
74
|
+
"duplicateEmailsAllowed",
|
|
75
|
+
"resetPasswordAllowed",
|
|
76
|
+
"verifyEmail",
|
|
77
|
+
"bruteForceProtected",
|
|
78
|
+
"passwordPolicy",
|
|
79
|
+
"otpPolicyType",
|
|
80
|
+
"accessTokenLifespan",
|
|
81
|
+
"ssoSessionIdleTimeout",
|
|
82
|
+
"ssoSessionMaxLifespan",
|
|
83
|
+
"eventsEnabled",
|
|
84
|
+
"adminEventsEnabled",
|
|
85
|
+
];
|
|
86
|
+
/** Safe subset of a realm representation, shared by realm_get and the realm resource. */
|
|
87
|
+
export function toRealmSettings(rep, realm) {
|
|
88
|
+
const data = {
|
|
89
|
+
realm: rep.realm ?? realm,
|
|
90
|
+
};
|
|
91
|
+
for (const key of REALM_SETTING_FIELDS) {
|
|
92
|
+
const value = rep[key];
|
|
93
|
+
if (value !== undefined && value !== null)
|
|
94
|
+
Object.assign(data, { [key]: value });
|
|
95
|
+
}
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
export const realmTools = [realmList, realmGet];
|
|
99
|
+
//# sourceMappingURL=realms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realms.js","sourceRoot":"","sources":["../../src/tools/realms.ts"],"names":[],"mappings":"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"}
|