Skip to content

Commit 4e4eb46

Browse files
feat(release): 1.0.44 adds pipeline jobs tool
1 parent 6869fae commit 4e4eb46

File tree

4 files changed

+398
-6
lines changed

4 files changed

+398
-6
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ When using with the Claude App, you need to set up your API key and URLs directl
7575
## Tools 🛠️
7676

7777
+<!-- TOOLS-START -->
78-
7978
1. `create_or_update_file` - Create or update a single file in a GitLab project
8079
2. `search_repositories` - Search for GitLab projects
8180
3. `create_repository` - Create a new GitLab project
@@ -93,7 +92,7 @@ When using with the Claude App, you need to set up your API key and URLs directl
9392
15. `mr_discussions` - List discussion items for a merge request
9493
16. `update_merge_request_note` - Modify an existing merge request thread note
9594
17. `create_merge_request_note` - Add a new note to an existing merge request thread
96-
18. `update_issue_note` - Update the content of an existing issue note
95+
18. `update_issue_note` - Modify an existing issue thread note
9796
19. `create_issue_note` - Add a new note to an existing issue thread
9897
20. `list_issues` - List issues in a GitLab project with filtering options
9998
21. `get_issue` - Get details of a specific issue in a GitLab project
@@ -121,4 +120,9 @@ When using with the Claude App, you need to set up your API key and URLs directl
121120
43. `update_wiki_page` - Update an existing wiki page in a GitLab project
122121
44. `delete_wiki_page` - Delete a wiki page from a GitLab project
123122
45. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
123+
46. `list_pipelines` - List pipelines in a GitLab project with filtering options
124+
47. `get_pipeline` - Get details of a specific pipeline in a GitLab project
125+
48. `list_pipeline_jobs` - List all jobs in a specific pipeline
126+
49. `get_pipeline_job` - Get details of a GitLab pipeline job number
127+
50. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
124128
<!-- TOOLS-END -->

index.ts

Lines changed: 273 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { fileURLToPath } from "url";
1717
import { dirname } from "path";
1818
import fs from "fs";
1919
import path from "path";
20-
2120
// Add type imports for proxy agents
2221
import { Agent } from "http";
2322
import { URL } from "url";
@@ -84,6 +83,15 @@ import {
8483
UpdateWikiPageSchema,
8584
DeleteWikiPageSchema,
8685
GitLabWikiPageSchema,
86+
GetRepositoryTreeSchema,
87+
GitLabTreeItemSchema,
88+
GitLabPipelineSchema,
89+
GetPipelineSchema,
90+
ListPipelinesSchema,
91+
ListPipelineJobsSchema,
92+
// pipeline job schemas
93+
GetPipelineJobOutputSchema,
94+
GitLabPipelineJobSchema,
8795
// Discussion Schemas
8896
GitLabDiscussionNoteSchema, // Added
8997
GitLabDiscussionSchema,
@@ -108,6 +116,11 @@ import {
108116
type GitLabNamespaceExistsResponse,
109117
type GitLabProject,
110118
type GitLabLabel,
119+
type GitLabPipeline,
120+
type ListPipelinesOptions,
121+
type GetPipelineOptions,
122+
type ListPipelineJobsOptions,
123+
type GitLabPipelineJob,
111124
// Discussion Types
112125
type GitLabDiscussionNote, // Added
113126
type GitLabDiscussion,
@@ -117,8 +130,6 @@ import {
117130
type UpdateWikiPageOptions,
118131
type DeleteWikiPageOptions,
119132
type GitLabWikiPage,
120-
GitLabTreeItemSchema,
121-
GetRepositoryTreeSchema,
122133
type GitLabTreeItem,
123134
type GetRepositoryTreeOptions,
124135
UpdateIssueNoteSchema,
@@ -430,6 +441,31 @@ const allTools = [
430441
"Get the repository tree for a GitLab project (list files and directories)",
431442
inputSchema: zodToJsonSchema(GetRepositoryTreeSchema),
432443
},
444+
{
445+
name: "list_pipelines",
446+
description: "List pipelines in a GitLab project with filtering options",
447+
inputSchema: zodToJsonSchema(ListPipelinesSchema),
448+
},
449+
{
450+
name: "get_pipeline",
451+
description: "Get details of a specific pipeline in a GitLab project",
452+
inputSchema: zodToJsonSchema(GetPipelineSchema),
453+
},
454+
{
455+
name: "list_pipeline_jobs",
456+
description: "List all jobs in a specific pipeline",
457+
inputSchema: zodToJsonSchema(ListPipelineJobsSchema),
458+
},
459+
{
460+
name: "get_pipeline_job",
461+
description: "Get details of a GitLab pipeline job number",
462+
inputSchema: zodToJsonSchema(GetPipelineJobOutputSchema),
463+
},
464+
{
465+
name: "get_pipeline_job_output",
466+
description: "Get the output/trace of a GitLab pipeline job number",
467+
inputSchema: zodToJsonSchema(GetPipelineJobOutputSchema),
468+
},
433469
];
434470

435471
// Define which tools are read-only
@@ -448,6 +484,11 @@ const readOnlyTools = [
448484
"get_namespace",
449485
"verify_namespace",
450486
"get_project",
487+
"get_pipeline",
488+
"list_pipelines",
489+
"list_pipeline_jobs",
490+
"get_pipeline_job",
491+
"get_pipeline_job_output",
451492
"list_projects",
452493
"list_labels",
453494
"get_label",
@@ -2300,6 +2341,166 @@ async function deleteWikiPage(projectId: string, slug: string): Promise<void> {
23002341
await handleGitLabError(response);
23012342
}
23022343

2344+
/**
2345+
* List pipelines in a GitLab project
2346+
*
2347+
* @param {string} projectId - The ID or URL-encoded path of the project
2348+
* @param {ListPipelinesOptions} options - Options for filtering pipelines
2349+
* @returns {Promise<GitLabPipeline[]>} List of pipelines
2350+
*/
2351+
async function listPipelines(
2352+
projectId: string,
2353+
options: Omit<ListPipelinesOptions, "project_id"> = {}
2354+
): Promise<GitLabPipeline[]> {
2355+
projectId = decodeURIComponent(projectId); // Decode project ID
2356+
const url = new URL(
2357+
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines`
2358+
);
2359+
2360+
// Add all query parameters
2361+
Object.entries(options).forEach(([key, value]) => {
2362+
if (value !== undefined) {
2363+
url.searchParams.append(key, value.toString());
2364+
}
2365+
});
2366+
2367+
const response = await fetch(url.toString(), {
2368+
...DEFAULT_FETCH_CONFIG,
2369+
});
2370+
2371+
await handleGitLabError(response);
2372+
const data = await response.json();
2373+
return z.array(GitLabPipelineSchema).parse(data);
2374+
}
2375+
2376+
/**
2377+
* Get details of a specific pipeline
2378+
*
2379+
* @param {string} projectId - The ID or URL-encoded path of the project
2380+
* @param {number} pipelineId - The ID of the pipeline
2381+
* @returns {Promise<GitLabPipeline>} Pipeline details
2382+
*/
2383+
async function getPipeline(
2384+
projectId: string,
2385+
pipelineId: number
2386+
): Promise<GitLabPipeline> {
2387+
projectId = decodeURIComponent(projectId); // Decode project ID
2388+
const url = new URL(
2389+
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}`
2390+
);
2391+
2392+
const response = await fetch(url.toString(), {
2393+
...DEFAULT_FETCH_CONFIG,
2394+
});
2395+
2396+
if (response.status === 404) {
2397+
throw new Error(`Pipeline not found`);
2398+
}
2399+
2400+
await handleGitLabError(response);
2401+
const data = await response.json();
2402+
return GitLabPipelineSchema.parse(data);
2403+
}
2404+
2405+
/**
2406+
* List all jobs in a specific pipeline
2407+
*
2408+
* @param {string} projectId - The ID or URL-encoded path of the project
2409+
* @param {number} pipelineId - The ID of the pipeline
2410+
* @param {Object} options - Options for filtering jobs
2411+
* @returns {Promise<GitLabPipelineJob[]>} List of pipeline jobs
2412+
*/
2413+
async function listPipelineJobs(
2414+
projectId: string,
2415+
pipelineId: number,
2416+
options: Omit<ListPipelineJobsOptions, "project_id" | "pipeline_id"> = {}
2417+
): Promise<GitLabPipelineJob[]> {
2418+
projectId = decodeURIComponent(projectId); // Decode project ID
2419+
const url = new URL(
2420+
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/jobs`
2421+
);
2422+
2423+
// Add all query parameters
2424+
Object.entries(options).forEach(([key, value]) => {
2425+
if (value !== undefined) {
2426+
if (typeof value === "boolean") {
2427+
url.searchParams.append(key, value ? "true" : "false");
2428+
} else {
2429+
url.searchParams.append(key, value.toString());
2430+
}
2431+
}
2432+
});
2433+
2434+
const response = await fetch(url.toString(), {
2435+
...DEFAULT_FETCH_CONFIG,
2436+
});
2437+
2438+
if (response.status === 404) {
2439+
throw new Error(`Pipeline not found`);
2440+
}
2441+
2442+
await handleGitLabError(response);
2443+
const data = await response.json();
2444+
return z.array(GitLabPipelineJobSchema).parse(data);
2445+
}
2446+
async function getPipelineJob(
2447+
projectId: string,
2448+
jobId: number
2449+
): Promise<GitLabPipelineJob> {
2450+
projectId = decodeURIComponent(projectId); // Decode project ID
2451+
const url = new URL(
2452+
`${GITLAB_API_URL}/projects/${encodeURIComponent(
2453+
projectId
2454+
)}/jobs/${jobId}`
2455+
);
2456+
2457+
const response = await fetch(url.toString(), {
2458+
...DEFAULT_FETCH_CONFIG,
2459+
});
2460+
2461+
if (response.status === 404) {
2462+
throw new Error(`Job not found`);
2463+
}
2464+
2465+
await handleGitLabError(response);
2466+
const data = await response.json();
2467+
return GitLabPipelineJobSchema.parse(data);
2468+
}
2469+
2470+
/**
2471+
* Get the output/trace of a pipeline job
2472+
*
2473+
* @param {string} projectId - The ID or URL-encoded path of the project
2474+
* @param {number} jobId - The ID of the job
2475+
* @returns {Promise<string>} The job output/trace
2476+
*/
2477+
async function getPipelineJobOutput(
2478+
projectId: string,
2479+
jobId: number
2480+
): Promise<string> {
2481+
projectId = decodeURIComponent(projectId); // Decode project ID
2482+
const url = new URL(
2483+
`${GITLAB_API_URL}/projects/${encodeURIComponent(
2484+
projectId
2485+
)}/jobs/${jobId}/trace`
2486+
);
2487+
2488+
const response = await fetch(url.toString(), {
2489+
...DEFAULT_FETCH_CONFIG,
2490+
headers: {
2491+
...DEFAULT_HEADERS,
2492+
Accept: "text/plain", // Override Accept header to get plain text
2493+
},
2494+
});
2495+
2496+
if (response.status === 404) {
2497+
throw new Error(`Job trace not found or job is not finished yet`);
2498+
}
2499+
2500+
await handleGitLabError(response);
2501+
return await response.text();
2502+
}
2503+
23032504
/**
23042505
* Get the repository tree for a project
23052506
* @param {string} projectId - The ID or URL-encoded path of the project
@@ -3030,6 +3231,75 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
30303231
};
30313232
}
30323233

3234+
case "list_pipelines": {
3235+
const args = ListPipelinesSchema.parse(request.params.arguments);
3236+
const { project_id, ...options } = args;
3237+
const pipelines = await listPipelines(project_id, options);
3238+
return {
3239+
content: [{ type: "text", text: JSON.stringify(pipelines, null, 2) }],
3240+
};
3241+
}
3242+
3243+
case "get_pipeline": {
3244+
const { project_id, pipeline_id } = GetPipelineSchema.parse(
3245+
request.params.arguments
3246+
);
3247+
const pipeline = await getPipeline(project_id, pipeline_id);
3248+
return {
3249+
content: [
3250+
{
3251+
type: "text",
3252+
text: JSON.stringify(pipeline, null, 2),
3253+
},
3254+
],
3255+
};
3256+
}
3257+
3258+
case "list_pipeline_jobs": {
3259+
const { project_id, pipeline_id, ...options } = ListPipelineJobsSchema.parse(
3260+
request.params.arguments
3261+
);
3262+
const jobs = await listPipelineJobs(project_id, pipeline_id, options);
3263+
return {
3264+
content: [
3265+
{
3266+
type: "text",
3267+
text: JSON.stringify(jobs, null, 2),
3268+
},
3269+
],
3270+
};
3271+
}
3272+
3273+
case "get_pipeline_job": {
3274+
const { project_id, job_id } = GetPipelineJobOutputSchema.parse(
3275+
request.params.arguments
3276+
);
3277+
const jobDetails = await getPipelineJob(project_id, job_id);
3278+
return {
3279+
content: [
3280+
{
3281+
type: "text",
3282+
text: JSON.stringify(jobDetails, null, 2),
3283+
},
3284+
],
3285+
};
3286+
}
3287+
3288+
case "get_pipeline_job_output": {
3289+
const { project_id, job_id } = GetPipelineJobOutputSchema.parse(
3290+
request.params.arguments
3291+
);
3292+
const jobOutput = await getPipelineJobOutput(project_id, job_id);
3293+
return {
3294+
content: [
3295+
{
3296+
type: "text",
3297+
text: jobOutput,
3298+
},
3299+
],
3300+
};
3301+
}
3302+
30333303
default:
30343304
throw new Error(`Unknown tool: ${request.params.name}`);
30353305
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zereight/mcp-gitlab",
3-
"version": "1.0.43",
3+
"version": "1.0.44",
44
"description": "MCP server for using the GitLab API",
55
"license": "MIT",
66
"author": "zereight",

0 commit comments

Comments
 (0)