@asanstefanski/everhour-mcp-server
Version:
Complete Everhour API integration for Model Context Protocol (MCP) with 100% endpoint coverage
402 lines • 14.7 kB
JavaScript
import { z } from 'zod';
// Zod schemas for input validation
const ListProjectsSchema = z.object({
page: z.number().optional(),
limit: z.number().optional(),
query: z.string().optional(),
status: z.enum(['active', 'archived', 'completed']).optional(),
client: z.number().optional(),
});
const GetProjectSchema = z.object({
id: z.string(),
});
const CreateProjectSchema = z.object({
name: z.string().min(1, 'Project name is required'),
client: z.number().optional(),
type: z.enum(['board', 'list']).optional(),
billing: z.object({
type: z.enum(['flat_rate', 'hourly_rate', 'none']).optional(),
budget: z.number().optional(),
rate: z.number().optional(),
}).optional(),
});
const UpdateProjectSchema = z.object({
id: z.string(),
name: z.string().optional(),
status: z.enum(['active', 'archived', 'completed']).optional(),
billing: z.object({
type: z.enum(['flat_rate', 'hourly_rate', 'none']).optional(),
budget: z.number().optional(),
rate: z.number().optional(),
}).optional(),
});
const DeleteProjectSchema = z.object({
id: z.string(),
});
// Helper function to format project output with all fields
const formatProject = (project) => ({
id: project.id,
name: project.name,
platform: project.platform,
createdAt: project.createdAt,
updatedAt: project.updatedAt,
workspaceId: project.workspaceId,
workspaceName: project.workspaceName,
hasWebhook: project.hasWebhook,
status: project.status,
type: project.type,
estimatesType: project.estimatesType,
canSyncTasks: project.canSyncTasks,
users: project.users,
attributes: project.attributes,
client: project.client,
billing: project.billing,
budget: project.budget,
rate: project.rate,
time: project.time,
isTemplate: project.isTemplate,
privacy: project.privacy,
connectionStatus: project.connectionStatus,
icon: project.icon,
color: project.color,
viewSettings: project.viewSettings,
foreign: project.foreign,
favorite: project.favorite,
});
export const projectTools = {
everhour_list_projects: {
name: 'everhour_list_projects',
description: 'List all projects from Everhour. Supports filtering by status, client, and search query.',
readonly: true,
operationType: 'read',
affectedResources: ['projects'],
inputSchema: {
type: 'object',
properties: {
page: {
type: 'number',
description: 'Page number for pagination (default: 1)',
},
limit: {
type: 'number',
description: 'Number of projects per page (default: 100)',
},
query: {
type: 'string',
description: 'Search query to filter projects by name',
},
status: {
type: 'string',
enum: ['active', 'archived', 'completed'],
description: 'Filter projects by status',
},
client: {
type: 'number',
description: 'Filter projects by client ID',
},
},
},
handler: async (client, args) => {
const params = ListProjectsSchema.parse(args);
try {
const projects = await client.getProjects(params);
return {
content: [
{
type: 'text',
text: JSON.stringify({
projects: projects.map(project => ({
id: project.id,
name: project.name,
status: project.status,
client: project.client,
type: project.type,
billing: project.billing,
time: project.time,
createdAt: project.createdAt,
updatedAt: project.updatedAt,
})),
total: projects.length,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error listing projects: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
},
},
everhour_get_project: {
name: 'everhour_get_project',
description: 'Get details of a specific project by ID.',
readonly: true,
operationType: 'read',
affectedResources: ['projects'],
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Project ID',
},
},
required: ['id'],
},
handler: async (client, args) => {
const { id } = GetProjectSchema.parse(args);
try {
const project = await client.getProject(id);
return {
content: [
{
type: 'text',
text: JSON.stringify({
project: {
id: project.id,
name: project.name,
status: project.status,
client: project.client,
type: project.type,
billing: project.billing,
time: project.time,
createdAt: project.createdAt,
updatedAt: project.updatedAt,
},
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting project: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
},
},
everhour_create_project: {
name: 'everhour_create_project',
description: 'Create a new project in Everhour.',
readonly: false,
operationType: 'write',
affectedResources: ['projects'],
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Project name',
},
client: {
type: 'number',
description: 'Client ID to associate with the project',
},
type: {
type: 'string',
enum: ['board', 'list'],
description: 'Project type (board or list)',
},
billing: {
type: 'object',
properties: {
type: {
type: 'string',
enum: ['flat_rate', 'hourly_rate', 'none'],
description: 'Billing type',
},
budget: {
type: 'number',
description: 'Project budget',
},
rate: {
type: 'number',
description: 'Hourly rate',
},
},
description: 'Billing configuration',
},
},
required: ['name'],
},
handler: async (client, args) => {
const params = CreateProjectSchema.parse(args);
try {
const project = await client.createProject(params);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
project: {
id: project.id,
name: project.name,
status: project.status,
client: project.client,
type: project.type,
billing: project.billing,
createdAt: project.createdAt,
},
message: `Project "${project.name}" created successfully`,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error creating project: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
},
},
everhour_update_project: {
name: 'everhour_update_project',
description: 'Update an existing project in Everhour.',
readonly: false,
operationType: 'write',
affectedResources: ['projects'],
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Project ID',
},
name: {
type: 'string',
description: 'New project name',
},
status: {
type: 'string',
enum: ['active', 'archived', 'completed'],
description: 'Project status',
},
billing: {
type: 'object',
properties: {
type: {
type: 'string',
enum: ['flat_rate', 'hourly_rate', 'none'],
description: 'Billing type',
},
budget: {
type: 'number',
description: 'Project budget',
},
rate: {
type: 'number',
description: 'Hourly rate',
},
},
description: 'Billing configuration',
},
},
required: ['id'],
},
handler: async (client, args) => {
const { id, ...updateParams } = UpdateProjectSchema.parse(args);
try {
const project = await client.updateProject(id, updateParams);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
project: {
id: project.id,
name: project.name,
status: project.status,
client: project.client,
type: project.type,
billing: project.billing,
updatedAt: project.updatedAt,
},
message: `Project "${project.name}" updated successfully`,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error updating project: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
},
},
everhour_delete_project: {
name: 'everhour_delete_project',
description: 'Delete a project from Everhour. This action cannot be undone.',
readonly: false,
operationType: 'delete',
affectedResources: ['projects'],
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Project ID',
},
},
required: ['id'],
},
handler: async (client, args) => {
const { id } = DeleteProjectSchema.parse(args);
try {
await client.deleteProject(id);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
message: `Project with ID "${id}" deleted successfully`,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error deleting project: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
};
}
},
},
};
//# sourceMappingURL=projects.js.map