redmine-mcp-tools
Version:
A comprehensive Model Context Protocol (MCP) server for Redmine integration. Provides 25+ specialized tools for complete Redmine API access including issue management, project administration, time tracking, and user management. Built with TypeScript and d
212 lines (211 loc) • 8.76 kB
JavaScript
import { asNumber, extractPaginationParams, ValidationError, } from "./types.js";
/**
* Creates handlers for user operations
*/
export function createUsersHandlers(context) {
const { client } = context;
return {
/**
* Lists users with pagination and filters
*/
list_users: async (args) => {
try {
if (typeof args !== 'object' || args === null) {
throw new ValidationError("Arguments must be an object");
}
const argsObj = args;
const { limit, offset } = extractPaginationParams(argsObj);
const queryParams = {
limit,
offset,
};
if ('status' in argsObj)
queryParams.status = asNumber(argsObj.status);
if ('name' in argsObj)
queryParams.name = String(argsObj.name);
if ('group_id' in argsObj)
queryParams.group_id = asNumber(argsObj.group_id);
const queryString = client.encodeQueryParams(queryParams);
const url = `users.json${queryString ? '?' + queryString : ''}`;
const response = await client.performRequest(url);
return {
content: [{
type: "text",
text: `Found ${response.total_count || 0} users (showing ${response.users?.length || 0}):\n\n` +
JSON.stringify(response, null, 2)
}]
};
}
catch (error) {
return {
content: [{
type: "text",
text: `Error listing users: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
},
/**
* Get a specific user
*/
show_user: async (args) => {
try {
if (typeof args !== 'object' || args === null) {
throw new ValidationError("Arguments must be an object");
}
const argsObj = args;
const userId = asNumber(argsObj.user_id);
const queryParams = {};
if ('include' in argsObj)
queryParams.include = String(argsObj.include);
const queryString = client.encodeQueryParams(queryParams);
const url = `users/${userId}.json${queryString ? '?' + queryString : ''}`;
const response = await client.performRequest(url);
return {
content: [{
type: "text",
text: `User #${userId}:\n\n` + JSON.stringify(response.user, null, 2)
}]
};
}
catch (error) {
return {
content: [{
type: "text",
text: `Error getting user: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
},
/**
* Create a new user
*/
create_user: async (args) => {
try {
if (typeof args !== 'object' || args === null) {
throw new ValidationError("Arguments must be an object");
}
const argsObj = args;
const userData = {
login: String(argsObj.login),
firstname: String(argsObj.firstname),
lastname: String(argsObj.lastname),
mail: String(argsObj.mail),
};
if ('password' in argsObj)
userData.password = String(argsObj.password);
if ('auth_source_id' in argsObj)
userData.auth_source_id = asNumber(argsObj.auth_source_id);
if ('mail_notification' in argsObj)
userData.mail_notification = String(argsObj.mail_notification);
if ('must_change_passwd' in argsObj)
userData.must_change_passwd = Boolean(argsObj.must_change_passwd);
if ('generate_password' in argsObj)
userData.generate_password = Boolean(argsObj.generate_password);
const response = await client.performRequest('users.json', {
method: 'POST',
body: JSON.stringify({ user: userData })
});
return {
content: [{
type: "text",
text: `User created successfully:\n\n` + JSON.stringify(response.user, null, 2)
}]
};
}
catch (error) {
return {
content: [{
type: "text",
text: `Error creating user: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
},
/**
* Update an existing user
*/
update_user: async (args) => {
try {
if (typeof args !== 'object' || args === null) {
throw new ValidationError("Arguments must be an object");
}
const argsObj = args;
const userId = asNumber(argsObj.user_id);
const userData = {};
if ('login' in argsObj)
userData.login = String(argsObj.login);
if ('firstname' in argsObj)
userData.firstname = String(argsObj.firstname);
if ('lastname' in argsObj)
userData.lastname = String(argsObj.lastname);
if ('mail' in argsObj)
userData.mail = String(argsObj.mail);
if ('password' in argsObj)
userData.password = String(argsObj.password);
if ('auth_source_id' in argsObj)
userData.auth_source_id = asNumber(argsObj.auth_source_id);
if ('mail_notification' in argsObj)
userData.mail_notification = String(argsObj.mail_notification);
if ('must_change_passwd' in argsObj)
userData.must_change_passwd = Boolean(argsObj.must_change_passwd);
if ('admin' in argsObj)
userData.admin = Boolean(argsObj.admin);
if ('status' in argsObj)
userData.status = asNumber(argsObj.status);
await client.performRequest(`users/${userId}.json`, {
method: 'PUT',
body: JSON.stringify({ user: userData })
});
return {
content: [{
type: "text",
text: `User #${userId} updated successfully`
}]
};
}
catch (error) {
return {
content: [{
type: "text",
text: `Error updating user: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
},
/**
* Delete a user
*/
delete_user: async (args) => {
try {
if (typeof args !== 'object' || args === null) {
throw new ValidationError("Arguments must be an object");
}
const argsObj = args;
const userId = asNumber(argsObj.user_id);
await client.performRequest(`users/${userId}.json`, {
method: 'DELETE'
});
return {
content: [{
type: "text",
text: `User #${userId} deleted successfully`
}]
};
}
catch (error) {
return {
content: [{
type: "text",
text: `Error deleting user: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
},
};
}