chenhunghan commited on
Commit
c691d96
·
unverified ·
1 Parent(s): 156cd64

feat: add patch user tool

Browse files

Signed-off-by: Hung-Han (Henry) Chen <chenhungh@gmail.com>

src/schemas/patchOperationSchema.ts ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * SCIM PATCH Operation Schema
5
+ * https://datatracker.ietf.org/doc/html/rfc7644#section-3.5.2
6
+ */
7
+ export const patchOperationSchema = {
8
+ schemas: z
9
+ .array(z.literal("urn:ietf:params:scim:api:messages:2.0:PatchOp"))
10
+ .describe("The SCIM schema URI for PATCH operations"),
11
+ Operations: z
12
+ .array(
13
+ z.object({
14
+ op: z
15
+ .enum(["add", "remove", "replace"])
16
+ .describe(
17
+ "The operation to perform. Valid values are 'add', 'remove', and 'replace'"
18
+ ),
19
+ path: z
20
+ .string()
21
+ .optional()
22
+ .describe(
23
+ "The attribute path describing the target of the operation. Required for 'remove' and 'replace' operations"
24
+ ),
25
+ value: z
26
+ .any()
27
+ .optional()
28
+ .describe(
29
+ "The value to be used for the operation. Required for 'add' and 'replace' operations"
30
+ ),
31
+ })
32
+ )
33
+ .describe("Array of PATCH operations to apply to the resource"),
34
+ };
src/tools/patchUser.ts ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { type InferSchema, type ToolMetadata } from "xmcp";
2
+ import { headers } from "xmcp/headers";
3
+ import { patchOperationSchema } from "../schemas/patchOperationSchema";
4
+ import { z } from "zod";
5
+
6
+ export const metadata: ToolMetadata = {
7
+ name: "patch-user",
8
+ description: "Partially update a user resource",
9
+ annotations: {
10
+ title: "Patch User Resource",
11
+ readOnlyHint: false,
12
+ destructiveHint: false,
13
+ idempotentHint: true,
14
+ openWorldHint: true,
15
+ },
16
+ };
17
+
18
+ export const schema = {
19
+ userId: z.string().describe("The unique identifier of the user to patch"),
20
+ ...patchOperationSchema,
21
+ };
22
+
23
+ export default async function patchUser(
24
+ params: InferSchema<typeof schema>
25
+ ) {
26
+ const requestHeaders = headers();
27
+ const apiToken = requestHeaders["x-scim-api-key"];
28
+ const baseUrl = requestHeaders["x-scim-base-url"];
29
+
30
+ if (!apiToken) {
31
+ throw new Error("Missing required headers: x-scim-api-key");
32
+ }
33
+
34
+ if (!baseUrl) {
35
+ throw new Error("Missing required headers: x-scim-base-url");
36
+ }
37
+
38
+ const { userId, ...patchOperation } = params;
39
+
40
+ const response = await fetch(`${baseUrl}/Users/${userId}`, {
41
+ method: "PATCH",
42
+ headers: {
43
+ "Content-Type": "application/scim+json",
44
+ Authorization: `Bearer ${apiToken}`,
45
+ },
46
+ body: JSON.stringify(patchOperation),
47
+ });
48
+
49
+ if (!response.ok) {
50
+ throw new Error(await response.text());
51
+ }
52
+
53
+ return {
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text: `User ${userId} patched successfully`,
58
+ },
59
+ {
60
+ type: "resource_link",
61
+ name: "User resource",
62
+ uri: `users://${userId}`,
63
+ },
64
+ ],
65
+ structuredContent: await response.json(),
66
+ };
67
+ }