feat: Gitlab list repository tree tool

This commit is contained in:
Bob
2025-05-07 14:11:37 +02:00
parent 8071fef6c4
commit bccd5f29c3
3 changed files with 74 additions and 6 deletions

View File

@ -113,6 +113,10 @@ import {
type UpdateWikiPageOptions, type UpdateWikiPageOptions,
type DeleteWikiPageOptions, type DeleteWikiPageOptions,
type GitLabWikiPage, type GitLabWikiPage,
GitLabTreeItemSchema,
GetRepositoryTreeSchema,
type GitLabTreeItem,
type GetRepositoryTreeOptions,
} from "./schemas.js"; } from "./schemas.js";
/** /**
@ -389,6 +393,11 @@ const allTools = [
description: "Delete a wiki page from a GitLab project", description: "Delete a wiki page from a GitLab project",
inputSchema: zodToJsonSchema(DeleteWikiPageSchema), inputSchema: zodToJsonSchema(DeleteWikiPageSchema),
}, },
{
name: "get_repository_tree",
description: "Get the repository tree for a GitLab project (list files and directories)",
inputSchema: zodToJsonSchema(GetRepositoryTreeSchema),
},
]; ];
// Define which tools are read-only // Define which tools are read-only
@ -2000,6 +2009,45 @@ async function deleteWikiPage(projectId: string, slug: string): Promise<void> {
await handleGitLabError(response); await handleGitLabError(response);
} }
/**
* Get the repository tree for a project
* @param {string} projectId - The ID or URL-encoded path of the project
* @param {GetRepositoryTreeOptions} options - Options for the tree
* @returns {Promise<GitLabTreeItem[]>}
*/
async function getRepositoryTree(
options: GetRepositoryTreeOptions
): Promise<GitLabTreeItem[]> {
const queryParams = new URLSearchParams();
if (options.path) queryParams.append("path", options.path);
if (options.ref) queryParams.append("ref", options.ref);
if (options.recursive) queryParams.append("recursive", "true");
if (options.per_page) queryParams.append("per_page", options.per_page.toString());
if (options.page_token) queryParams.append("page_token", options.page_token);
if (options.pagination) queryParams.append("pagination", options.pagination);
const response = await fetch(
`${GITLAB_API_URL}/projects/${encodeURIComponent(options.project_id)}/repository/tree?${queryParams.toString()}`,
{
headers: {
Authorization: `Bearer ${GITLAB_PERSONAL_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
}
);
if (response.status === 404) {
throw new Error("Repository or path not found");
}
if (!response.ok) {
throw new Error(`Failed to get repository tree: ${response.statusText}`);
}
const data = await response.json();
return z.array(GitLabTreeItemSchema).parse(data);
}
server.setRequestHandler(ListToolsRequestSchema, async () => { server.setRequestHandler(ListToolsRequestSchema, async () => {
// Apply read-only filter first // Apply read-only filter first
const tools0 = GITLAB_READ_ONLY_MODE const tools0 = GITLAB_READ_ONLY_MODE
@ -2579,6 +2627,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
}; };
} }
case "get_repository_tree": {
const args = GetRepositoryTreeSchema.parse(request.params.arguments);
const tree = await getRepositoryTree(args);
return {
content: [{ type: "text", text: JSON.stringify(tree, null, 2) }],
};
}
default: default:
throw new Error(`Unknown tool: ${request.params.name}`); throw new Error(`Unknown tool: ${request.params.name}`);
} }

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.32", "version": "1.0.33",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.32", "version": "1.0.33",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "1.8.0", "@modelcontextprotocol/sdk": "1.8.0",

View File

@ -160,17 +160,27 @@ export const FileOperationSchema = z.object({
}); });
// Tree and commit schemas // Tree and commit schemas
export const GitLabTreeEntrySchema = z.object({ export const GitLabTreeItemSchema = z.object({
id: z.string(), // Changed from sha to match GitLab API id: z.string(),
name: z.string(), name: z.string(),
type: z.enum(["blob", "tree"]), type: z.enum(["tree", "blob"]),
path: z.string(), path: z.string(),
mode: z.string(), mode: z.string(),
}); });
export const GetRepositoryTreeSchema = z.object({
project_id: z.string().describe("The ID or URL-encoded path of the project"),
path: z.string().optional().describe("The path inside the repository"),
ref: z.string().optional().describe("The name of a repository branch or tag. Defaults to the default branch."),
recursive: z.boolean().optional().describe("Boolean value to get a recursive tree"),
per_page: z.number().optional().describe("Number of results to show per page"),
page_token: z.string().optional().describe("The tree record ID for pagination"),
pagination: z.string().optional().describe("Pagination method (keyset)")
});
export const GitLabTreeSchema = z.object({ export const GitLabTreeSchema = z.object({
id: z.string(), // Changed from sha to match GitLab API id: z.string(), // Changed from sha to match GitLab API
tree: z.array(GitLabTreeEntrySchema), tree: z.array(GitLabTreeItemSchema),
}); });
export const GitLabCommitSchema = z.object({ export const GitLabCommitSchema = z.object({
@ -1033,3 +1043,5 @@ export type CreateWikiPageOptions = z.infer<typeof CreateWikiPageSchema>;
export type UpdateWikiPageOptions = z.infer<typeof UpdateWikiPageSchema>; export type UpdateWikiPageOptions = z.infer<typeof UpdateWikiPageSchema>;
export type DeleteWikiPageOptions = z.infer<typeof DeleteWikiPageSchema>; export type DeleteWikiPageOptions = z.infer<typeof DeleteWikiPageSchema>;
export type GitLabWikiPage = z.infer<typeof GitLabWikiPageSchema>; export type GitLabWikiPage = z.infer<typeof GitLabWikiPageSchema>;
export type GitLabTreeItem = z.infer<typeof GitLabTreeItemSchema>;
export type GetRepositoryTreeOptions = z.infer<typeof GetRepositoryTreeSchema>;