mcp-server-linear
Version:
An MCP server for interacting with Linear's API, providing tools for managing issues, projects, and teams
180 lines • 6.77 kB
JavaScript
import { BaseHandler } from "../../../core/handlers/base.handler.js";
/**
* Handler for project-related operations.
* Manages creating, searching, and retrieving project information.
*/
export class ProjectHandler extends BaseHandler {
constructor(auth, graphqlClient) {
super(auth, graphqlClient);
}
/**
* Creates a new project with associated issues.
*/
/**
* Creates a new project with associated issues
* @example
* ```typescript
* const result = await handler.handleCreateProjectWithIssues({
* project: {
* name: "Q1 Planning",
* description: "Q1 2025 Planning Project",
* teamIds: ["team-id-1"], // Required: Array of team IDs
* },
* issues: [{
* title: "Project Setup",
* description: "Initial project setup tasks",
* teamId: "team-id-1"
* }]
* });
* ```
*/
async handleCreateProjectWithIssues(args) {
try {
const client = this.verifyAuth();
this.validateRequiredParams(args, ["project", "issues"]);
// Validate project input
if (!args.project.teamIds ||
!Array.isArray(args.project.teamIds) ||
args.project.teamIds.length === 0) {
throw new Error("Project requires teamIds as an array with at least one team ID.\n" +
"Example:\n" +
"{\n" +
" project: {\n" +
' name: "Project Name",\n' +
' teamIds: ["team-id-1"]\n' +
" },\n" +
' issues: [{ title: "Issue Title", teamId: "team-id-1" }]\n' +
"}");
}
if (!Array.isArray(args.issues)) {
throw new Error("Issues parameter must be an array of issue objects.\n" +
'Example: issues: [{ title: "Issue Title", teamId: "team-id-1" }]');
}
// Validate each issue has required teamId
args.issues.forEach((issue, index) => {
if (!issue.teamId) {
throw new Error(`Issue at index ${index} is missing required teamId.\n` +
"Each issue must have a teamId that matches one of the project teamIds.");
}
});
const result = await client.createProjectWithIssues(args.project, args.issues);
if (!result.projectCreate.success ||
(result.issueBatchCreate && !result.issueBatchCreate.success)) {
throw new Error("Failed to create project or issues");
}
const { project } = result.projectCreate;
const issuesCreated = result.issueBatchCreate?.issues.length ?? 0;
const response = [
`Successfully created project with issues`,
`Project: ${project.name}`,
`Project URL: ${project.url}`,
];
if (issuesCreated > 0) {
response.push(`Issues created: ${issuesCreated}`);
// Add details for each issue
result.issueBatchCreate?.issues.forEach((issue) => {
response.push(`- ${issue.identifier}: ${issue.title} (${issue.url})`);
});
}
return this.createResponse(response.join("\n"));
}
catch (error) {
this.handleError(error, "create project with issues");
}
}
/**
* Gets information about a specific project.
*/
async handleGetProject(args) {
try {
const client = this.verifyAuth();
this.validateRequiredParams(args, ["id"]);
const result = await client.getProject(args.id);
return this.createJsonResponse(result);
}
catch (error) {
this.handleError(error, "get project info");
}
}
/**
* Lists all projects or searches with optional filters.
*/
async handleListProjects(args = {}) {
try {
const client = this.verifyAuth();
const result = await client.searchProjects(args.filter);
return this.createJsonResponse(result);
}
catch (error) {
this.handleError(error, "list projects");
}
}
/**
* Get project milestones with filtering and pagination
*/
async handleGetProjectMilestones(args) {
try {
const client = this.verifyAuth();
this.validateRequiredParams(args, ["projectId"]);
const result = await client.getProjectMilestones(args.projectId, args.filter, args.first, args.after, args.last, args.before, args.includeArchived, args.orderBy);
return this.createJsonResponse(result);
}
catch (error) {
this.handleError(error, "get project milestones");
}
}
/**
* Create a new project milestone
*/
async handleCreateProjectMilestone(args) {
try {
const client = this.verifyAuth();
this.validateRequiredParams(args, ["projectId", "name"]);
const result = await client.createProjectMilestone({
projectId: args.projectId,
name: args.name,
description: args.description,
targetDate: args.targetDate,
sortOrder: args.sortOrder,
});
return this.createJsonResponse(result);
}
catch (error) {
this.handleError(error, "create project milestone");
}
}
/**
* Update a project milestone
*/
async handleUpdateProjectMilestone(args) {
try {
const client = this.verifyAuth();
this.validateRequiredParams(args, ["id"]);
const result = await client.updateProjectMilestone(args.id, {
name: args.name,
description: args.description,
targetDate: args.targetDate,
sortOrder: args.sortOrder,
});
return this.createJsonResponse(result);
}
catch (error) {
this.handleError(error, "update project milestone");
}
}
/**
* Delete a project milestone
*/
async handleDeleteProjectMilestone(args) {
try {
const client = this.verifyAuth();
this.validateRequiredParams(args, ["id"]);
const result = await client.deleteProjectMilestone(args.id);
return this.createJsonResponse(result);
}
catch (error) {
this.handleError(error, "delete project milestone");
}
}
}
//# sourceMappingURL=project.handler.js.map