feat: add MCP server for AI assistant integration (#5882)
This commit is contained in:
1 parent
d77c6f8d4e
commit
6baff7be3d
47 files changed
+5722
-12624
No files matched your search
+143
-16
@@ -144,7 +144,19 @@ export const userRouter = createTRPCRouter({
|
||||
}),
|
||||
create: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "POST",
|
||||
path: "/api/users",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
mcp: {
|
||||
enabled: true,
|
||||
description:
|
||||
"Create a new user account (admin only). REQUIRED: username (string), password (string), confirmPassword (must match password). OPTIONAL: email (string or null)",
|
||||
},
|
||||
})
|
||||
.input(convertIntersectionToZodObject(userCreateSchema))
|
||||
.output(z.void())
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
@@ -159,7 +171,14 @@ export const userRouter = createTRPCRouter({
|
||||
}),
|
||||
setProfileImage: protectedProcedure
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PUT", path: "/api/users/profileImage", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PUT",
|
||||
path: "/api/users/profileImage",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
userId: z.string(),
|
||||
@@ -205,8 +224,26 @@ export const userRouter = createTRPCRouter({
|
||||
getAll: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
.input(z.void())
|
||||
.output(z.array(selectUserSchema.pick({ id: true, name: true, email: true, emailVerified: true, image: true })))
|
||||
.meta({ openapi: { method: "GET", path: "/api/users", tags: ["users"], protect: true } })
|
||||
.output(
|
||||
z.array(
|
||||
selectUserSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
emailVerified: true,
|
||||
image: true,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "GET",
|
||||
path: "/api/users",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
mcp: { enabled: true, description: "List all users (admin only)" },
|
||||
})
|
||||
.query(({ ctx }) => {
|
||||
return ctx.db.query.users.findMany({
|
||||
columns: {
|
||||
@@ -221,8 +258,24 @@ export const userRouter = createTRPCRouter({
|
||||
// Is protected because also used in board access / integration access forms
|
||||
selectable: protectedProcedure
|
||||
.input(z.object({ excludeExternalProviders: z.boolean().default(false) }).optional())
|
||||
.output(z.array(selectUserSchema.pick({ id: true, name: true, image: true, email: true })))
|
||||
.meta({ openapi: { method: "GET", path: "/api/users/selectable", tags: ["users"], protect: true } })
|
||||
.output(
|
||||
z.array(
|
||||
selectUserSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
image: true,
|
||||
email: true,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "GET",
|
||||
path: "/api/users/selectable",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.query(({ ctx, input }) => {
|
||||
return ctx.db.query.users.findMany({
|
||||
columns: {
|
||||
@@ -242,8 +295,24 @@ export const userRouter = createTRPCRouter({
|
||||
limit: z.number().min(1).max(100).default(10),
|
||||
}),
|
||||
)
|
||||
.output(z.array(selectUserSchema.pick({ id: true, name: true, image: true, email: true })))
|
||||
.meta({ openapi: { method: "POST", path: "/api/users/search", tags: ["users"], protect: true } })
|
||||
.output(
|
||||
z.array(
|
||||
selectUserSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
image: true,
|
||||
email: true,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "POST",
|
||||
path: "/api/users/search",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.query(async ({ input, ctx }) => {
|
||||
const dbUsers = await ctx.db.query.users.findMany({
|
||||
columns: {
|
||||
@@ -283,7 +352,15 @@ export const userRouter = createTRPCRouter({
|
||||
completedBoardTour: true,
|
||||
}),
|
||||
)
|
||||
.meta({ openapi: { method: "GET", path: "/api/users/{userId}", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "GET",
|
||||
path: "/api/users/{userId}",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
mcp: { enabled: true, description: "Get user details by user ID. REQUIRED: userId (string)" },
|
||||
})
|
||||
.query(async ({ input, ctx }) => {
|
||||
// Only admins can view other users details
|
||||
if (ctx.session.user.id !== input.userId && !ctx.session.user.permissions.includes("admin")) {
|
||||
@@ -325,7 +402,14 @@ export const userRouter = createTRPCRouter({
|
||||
editProfile: protectedProcedure
|
||||
.input(userEditProfileSchema)
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PUT", path: "/api/users/profile", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PUT",
|
||||
path: "/api/users/profile",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
// Only admins can view other users details
|
||||
if (ctx.session.user.id !== input.id && !ctx.session.user.permissions.includes("admin")) {
|
||||
@@ -369,7 +453,15 @@ export const userRouter = createTRPCRouter({
|
||||
delete: protectedProcedure
|
||||
.input(z.object({ userId: z.string() }))
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "DELETE", path: "/api/users/{userId}", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "DELETE",
|
||||
path: "/api/users/{userId}",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
mcp: { enabled: true, description: "Delete a user by ID. REQUIRED: userId (string)" },
|
||||
})
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
// Only admins and user itself can delete a user
|
||||
if (ctx.session.user.id !== input.userId && !ctx.session.user.permissions.includes("admin")) {
|
||||
@@ -384,7 +476,14 @@ export const userRouter = createTRPCRouter({
|
||||
changePassword: protectedProcedure
|
||||
.input(convertIntersectionToZodObject(userChangePasswordApiSchema))
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PATCH", path: "/api/users/{userId}/changePassword", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PATCH",
|
||||
path: "/api/users/{userId}/changePassword",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const user = ctx.session.user;
|
||||
// Only admins can change other users' passwords
|
||||
@@ -449,7 +548,14 @@ export const userRouter = createTRPCRouter({
|
||||
changeHomeBoards: protectedProcedure
|
||||
.input(convertIntersectionToZodObject(userChangeHomeBoardsSchema.and(z.object({ userId: z.string() }))))
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PATCH", path: "/api/users/changeHome", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PATCH",
|
||||
path: "/api/users/changeHome",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const user = ctx.session.user;
|
||||
// Only admins can change other users passwords
|
||||
@@ -515,14 +621,28 @@ export const userRouter = createTRPCRouter({
|
||||
changeSearchPreferences: protectedProcedure
|
||||
.input(convertIntersectionToZodObject(changeSearchPreferencesInputSchema))
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PATCH", path: "/api/users/search-preferences", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PATCH",
|
||||
path: "/api/users/search-preferences",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await changeSearchPreferencesAsync(ctx.db, ctx.session, input);
|
||||
}),
|
||||
changeColorScheme: protectedProcedure
|
||||
.input(userChangeColorSchemeSchema)
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PATCH", path: "/api/users/changeScheme", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PATCH",
|
||||
path: "/api/users/changeScheme",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
await ctx.db
|
||||
.update(users)
|
||||
@@ -579,7 +699,14 @@ export const userRouter = createTRPCRouter({
|
||||
changeFirstDayOfWeek: protectedProcedure
|
||||
.input(convertIntersectionToZodObject(userFirstDayOfWeekSchema.and(byIdSchema)))
|
||||
.output(z.void())
|
||||
.meta({ openapi: { method: "PATCH", path: "/api/users/firstDayOfWeek", tags: ["users"], protect: true } })
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "PATCH",
|
||||
path: "/api/users/firstDayOfWeek",
|
||||
tags: ["users"],
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
// Only admins can change other users first day of week
|
||||
if (!ctx.session.user.permissions.includes("admin") && ctx.session.user.id !== input.id) {
|
||||
|
||||
Reference in new issue
Block a user