Build upd.
This commit is contained in:
205
build/index.js
205
build/index.js
@ -9,7 +9,7 @@ import { fileURLToPath } from "url";
|
||||
import { dirname } from "path";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListIssuesSchema, GetIssueSchema, UpdateIssueSchema, DeleteIssueSchema, GitLabIssueLinkSchema, GitLabIssueWithLinkDetailsSchema, ListIssueLinksSchema, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, CreateNoteSchema, } from "./schemas.js";
|
||||
import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListIssuesSchema, GetIssueSchema, UpdateIssueSchema, DeleteIssueSchema, GitLabIssueLinkSchema, GitLabIssueWithLinkDetailsSchema, ListIssueLinksSchema, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, ListLabelsSchema, GetLabelSchema, CreateLabelSchema, UpdateLabelSchema, DeleteLabelSchema, CreateNoteSchema, } from "./schemas.js";
|
||||
/**
|
||||
* Read version from package.json
|
||||
*/
|
||||
@ -826,19 +826,139 @@ async function getProject(projectId, options = {}) {
|
||||
* @returns {Promise<GitLabProject[]>} List of projects
|
||||
*/
|
||||
async function listProjects(options = {}) {
|
||||
const url = new URL(`${GITLAB_API_URL}/projects`);
|
||||
// Add all the query parameters from options
|
||||
// Construct the query parameters
|
||||
const params = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(options)) {
|
||||
if (value !== undefined && value !== null) {
|
||||
if (typeof value === "boolean") {
|
||||
params.append(key, value ? "true" : "false");
|
||||
}
|
||||
else {
|
||||
params.append(key, String(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make the API request
|
||||
const response = await fetch(`${GITLAB_API_URL}/projects?${params.toString()}`, {
|
||||
method: "GET",
|
||||
headers: DEFAULT_HEADERS,
|
||||
});
|
||||
// Handle errors
|
||||
await handleGitLabError(response);
|
||||
// Parse and return the data
|
||||
const data = await response.json();
|
||||
return z.array(GitLabProjectSchema).parse(data);
|
||||
}
|
||||
/**
|
||||
* List labels for a project
|
||||
*
|
||||
* @param projectId The ID or URL-encoded path of the project
|
||||
* @param options Optional parameters for listing labels
|
||||
* @returns Array of GitLab labels
|
||||
*/
|
||||
async function listLabels(projectId, options = {}) {
|
||||
// Construct the URL with project path
|
||||
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`);
|
||||
// Add query parameters
|
||||
Object.entries(options).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
url.searchParams.append(key, value.toString());
|
||||
if (typeof value === "boolean") {
|
||||
url.searchParams.append(key, value ? "true" : "false");
|
||||
}
|
||||
else {
|
||||
url.searchParams.append(key, String(value));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Make the API request
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: DEFAULT_HEADERS,
|
||||
});
|
||||
// Handle errors
|
||||
await handleGitLabError(response);
|
||||
// Parse and return the data
|
||||
const data = await response.json();
|
||||
return z.array(GitLabRepositorySchema).parse(data);
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Get a single label from a project
|
||||
*
|
||||
* @param projectId The ID or URL-encoded path of the project
|
||||
* @param labelId The ID or name of the label
|
||||
* @param includeAncestorGroups Whether to include ancestor groups
|
||||
* @returns GitLab label
|
||||
*/
|
||||
async function getLabel(projectId, labelId, includeAncestorGroups) {
|
||||
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`);
|
||||
// Add query parameters
|
||||
if (includeAncestorGroups !== undefined) {
|
||||
url.searchParams.append("include_ancestor_groups", includeAncestorGroups ? "true" : "false");
|
||||
}
|
||||
// Make the API request
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: DEFAULT_HEADERS,
|
||||
});
|
||||
// Handle errors
|
||||
await handleGitLabError(response);
|
||||
// Parse and return the data
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Create a new label in a project
|
||||
*
|
||||
* @param projectId The ID or URL-encoded path of the project
|
||||
* @param options Options for creating the label
|
||||
* @returns Created GitLab label
|
||||
*/
|
||||
async function createLabel(projectId, options) {
|
||||
// Make the API request
|
||||
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels`, {
|
||||
method: "POST",
|
||||
headers: DEFAULT_HEADERS,
|
||||
body: JSON.stringify(options),
|
||||
});
|
||||
// Handle errors
|
||||
await handleGitLabError(response);
|
||||
// Parse and return the data
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Update an existing label in a project
|
||||
*
|
||||
* @param projectId The ID or URL-encoded path of the project
|
||||
* @param labelId The ID or name of the label to update
|
||||
* @param options Options for updating the label
|
||||
* @returns Updated GitLab label
|
||||
*/
|
||||
async function updateLabel(projectId, labelId, options) {
|
||||
// Make the API request
|
||||
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
|
||||
method: "PUT",
|
||||
headers: DEFAULT_HEADERS,
|
||||
body: JSON.stringify(options),
|
||||
});
|
||||
// Handle errors
|
||||
await handleGitLabError(response);
|
||||
// Parse and return the data
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Delete a label from a project
|
||||
*
|
||||
* @param projectId The ID or URL-encoded path of the project
|
||||
* @param labelId The ID or name of the label to delete
|
||||
*/
|
||||
async function deleteLabel(projectId, labelId) {
|
||||
// Make the API request
|
||||
const response = await fetch(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/labels/${encodeURIComponent(String(labelId))}`, {
|
||||
method: "DELETE",
|
||||
headers: DEFAULT_HEADERS,
|
||||
});
|
||||
// Handle errors
|
||||
await handleGitLabError(response);
|
||||
}
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
@ -973,6 +1093,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
description: "List projects accessible by the current user",
|
||||
inputSchema: zodToJsonSchema(ListProjectsSchema),
|
||||
},
|
||||
{
|
||||
name: "list_labels",
|
||||
description: "List labels for a project",
|
||||
inputSchema: zodToJsonSchema(ListLabelsSchema),
|
||||
},
|
||||
{
|
||||
name: "get_label",
|
||||
description: "Get a single label from a project",
|
||||
inputSchema: zodToJsonSchema(GetLabelSchema),
|
||||
},
|
||||
{
|
||||
name: "create_label",
|
||||
description: "Create a new label in a project",
|
||||
inputSchema: zodToJsonSchema(CreateLabelSchema),
|
||||
},
|
||||
{
|
||||
name: "update_label",
|
||||
description: "Update an existing label in a project",
|
||||
inputSchema: zodToJsonSchema(UpdateLabelSchema),
|
||||
},
|
||||
{
|
||||
name: "delete_label",
|
||||
description: "Delete a label from a project",
|
||||
inputSchema: zodToJsonSchema(DeleteLabelSchema),
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
@ -1162,19 +1307,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
}
|
||||
case "list_projects": {
|
||||
const args = ListProjectsSchema.parse(request.params.arguments);
|
||||
const url = new URL(`${GITLAB_API_URL}/projects`);
|
||||
// Add query parameters for filtering
|
||||
Object.entries(args).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
url.searchParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: DEFAULT_HEADERS,
|
||||
});
|
||||
await handleGitLabError(response);
|
||||
const data = await response.json();
|
||||
const projects = z.array(GitLabProjectSchema).parse(data);
|
||||
const projects = await listProjects(args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
|
||||
};
|
||||
@ -1245,6 +1378,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
content: [{ type: "text", text: JSON.stringify({ status: "success", message: "Issue link deleted successfully" }, null, 2) }],
|
||||
};
|
||||
}
|
||||
case "list_labels": {
|
||||
const args = ListLabelsSchema.parse(request.params.arguments);
|
||||
const labels = await listLabels(args.project_id, args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(labels, null, 2) }],
|
||||
};
|
||||
}
|
||||
case "get_label": {
|
||||
const args = GetLabelSchema.parse(request.params.arguments);
|
||||
const label = await getLabel(args.project_id, args.label_id, args.include_ancestor_groups);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
|
||||
};
|
||||
}
|
||||
case "create_label": {
|
||||
const args = CreateLabelSchema.parse(request.params.arguments);
|
||||
const label = await createLabel(args.project_id, args);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
|
||||
};
|
||||
}
|
||||
case "update_label": {
|
||||
const args = UpdateLabelSchema.parse(request.params.arguments);
|
||||
const { project_id, label_id, ...options } = args;
|
||||
const label = await updateLabel(project_id, label_id, options);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify(label, null, 2) }],
|
||||
};
|
||||
}
|
||||
case "delete_label": {
|
||||
const args = DeleteLabelSchema.parse(request.params.arguments);
|
||||
await deleteLabel(args.project_id, args.label_id);
|
||||
return {
|
||||
content: [{ type: "text", text: JSON.stringify({ status: "success", message: "Label deleted successfully" }, null, 2) }],
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||
}
|
||||
|
@ -207,7 +207,15 @@ export const GitLabLabelSchema = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
description: z.string().optional(),
|
||||
text_color: z.string(),
|
||||
description: z.string().nullable(),
|
||||
description_html: z.string().nullable(),
|
||||
open_issues_count: z.number().optional(),
|
||||
closed_issues_count: z.number().optional(),
|
||||
open_merge_requests_count: z.number().optional(),
|
||||
subscribed: z.boolean().optional(),
|
||||
priority: z.number().nullable().optional(),
|
||||
is_project_label: z.boolean().optional(),
|
||||
});
|
||||
export const GitLabUserSchema = z.object({
|
||||
username: z.string(), // Changed from login to match GitLab API
|
||||
@ -295,7 +303,7 @@ export const GitLabMergeRequestSchema = z.object({
|
||||
assignees: z.array(GitLabUserSchema).optional(),
|
||||
source_branch: z.string(),
|
||||
target_branch: z.string(),
|
||||
diff_refs: GitLabMergeRequestDiffRefSchema.optional(),
|
||||
diff_refs: GitLabMergeRequestDiffRefSchema.nullable().optional(),
|
||||
web_url: z.string(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
@ -308,10 +316,10 @@ export const GitLabMergeRequestSchema = z.object({
|
||||
work_in_progress: z.boolean().optional(),
|
||||
blocking_discussions_resolved: z.boolean().optional(),
|
||||
should_remove_source_branch: z.boolean().nullable().optional(),
|
||||
force_remove_source_branch: z.boolean().optional(),
|
||||
force_remove_source_branch: z.boolean().nullable().optional(),
|
||||
allow_collaboration: z.boolean().optional(),
|
||||
allow_maintainer_to_push: z.boolean().optional(),
|
||||
changes_count: z.string().optional(),
|
||||
changes_count: z.string().nullable().optional(),
|
||||
merge_when_pipeline_succeeds: z.boolean().optional(),
|
||||
squash: z.boolean().optional(),
|
||||
labels: z.array(z.string()).optional(),
|
||||
@ -558,3 +566,34 @@ export const ListProjectsSchema = z.object({
|
||||
with_merge_requests_enabled: z.boolean().optional().describe("Filter projects with merge requests feature enabled"),
|
||||
min_access_level: z.number().optional().describe("Filter by minimum access level"),
|
||||
});
|
||||
// Label operation schemas
|
||||
export const ListLabelsSchema = z.object({
|
||||
project_id: z.string().describe("Project ID or URL-encoded path"),
|
||||
with_counts: z.boolean().optional().describe("Whether or not to include issue and merge request counts"),
|
||||
include_ancestor_groups: z.boolean().optional().describe("Include ancestor groups"),
|
||||
search: z.string().optional().describe("Keyword to filter labels by"),
|
||||
});
|
||||
export const GetLabelSchema = z.object({
|
||||
project_id: z.string().describe("Project ID or URL-encoded path"),
|
||||
label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"),
|
||||
include_ancestor_groups: z.boolean().optional().describe("Include ancestor groups"),
|
||||
});
|
||||
export const CreateLabelSchema = z.object({
|
||||
project_id: z.string().describe("Project ID or URL-encoded path"),
|
||||
name: z.string().describe("The name of the label"),
|
||||
color: z.string().describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
|
||||
description: z.string().optional().describe("The description of the label"),
|
||||
priority: z.number().nullable().optional().describe("The priority of the label"),
|
||||
});
|
||||
export const UpdateLabelSchema = z.object({
|
||||
project_id: z.string().describe("Project ID or URL-encoded path"),
|
||||
label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"),
|
||||
new_name: z.string().optional().describe("The new name of the label"),
|
||||
color: z.string().optional().describe("The color of the label given in 6-digit hex notation with leading '#' sign"),
|
||||
description: z.string().optional().describe("The new description of the label"),
|
||||
priority: z.number().nullable().optional().describe("The new priority of the label"),
|
||||
});
|
||||
export const DeleteLabelSchema = z.object({
|
||||
project_id: z.string().describe("Project ID or URL-encoded path"),
|
||||
label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"),
|
||||
});
|
||||
|
54
build/test-note.js
Normal file
54
build/test-note.js
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* This test file verifies that the createNote function works correctly
|
||||
* with the fixed endpoint URL construction that uses plural resource names
|
||||
* (issues instead of issue, merge_requests instead of merge_request).
|
||||
*/
|
||||
import fetch from "node-fetch";
|
||||
// GitLab API configuration (replace with actual values when testing)
|
||||
const GITLAB_API_URL = process.env.GITLAB_API_URL || "https://gitlab.com";
|
||||
const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_TOKEN || "";
|
||||
const PROJECT_ID = process.env.PROJECT_ID || "your/project";
|
||||
const ISSUE_IID = Number(process.env.ISSUE_IID || "1");
|
||||
async function testCreateIssueNote() {
|
||||
try {
|
||||
// Using plural form "issues" in the URL
|
||||
const url = new URL(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(PROJECT_ID)}/issues/${ISSUE_IID}/notes`);
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${GITLAB_PERSONAL_ACCESS_TOKEN}`,
|
||||
},
|
||||
body: JSON.stringify({ body: "Test note from API - with plural endpoint" }),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorBody = await response.text();
|
||||
throw new Error(`GitLab API error: ${response.status} ${response.statusText}\n${errorBody}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
console.log("Successfully created note:");
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error creating note:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Only run the test if executed directly
|
||||
if (require.main === module) {
|
||||
console.log("Testing note creation with plural 'issues' endpoint...");
|
||||
testCreateIssueNote().then(success => {
|
||||
if (success) {
|
||||
console.log("✅ Test successful!");
|
||||
process.exit(0);
|
||||
}
|
||||
else {
|
||||
console.log("❌ Test failed!");
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Export for use in other tests
|
||||
export { testCreateIssueNote };
|
Reference in New Issue
Block a user