Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ import {
type UpdateWikiPageOptions,
type DeleteWikiPageOptions,
type GitLabWikiPage,
GitLabTreeItemSchema,
GetRepositoryTreeSchema,
type GitLabTreeItem,
type GetRepositoryTreeOptions,
} from "./schemas.js";

/**
Expand Down Expand Up @@ -389,6 +393,11 @@ const allTools = [
description: "Delete a wiki page from a GitLab project",
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
Expand Down Expand Up @@ -2000,6 +2009,45 @@ async function deleteWikiPage(projectId: string, slug: string): Promise<void> {
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 () => {
// Apply read-only filter first
const tools0 = GITLAB_READ_ONLY_MODE
Expand Down Expand Up @@ -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:
throw new Error(`Unknown tool: ${request.params.name}`);
}
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 16 additions & 4 deletions schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,27 @@ export const FileOperationSchema = z.object({
});

// Tree and commit schemas
export const GitLabTreeEntrySchema = z.object({
id: z.string(), // Changed from sha to match GitLab API
export const GitLabTreeItemSchema = z.object({
id: z.string(),
name: z.string(),
type: z.enum(["blob", "tree"]),
type: z.enum(["tree", "blob"]),
path: 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({
id: z.string(), // Changed from sha to match GitLab API
tree: z.array(GitLabTreeEntrySchema),
tree: z.array(GitLabTreeItemSchema),
});

export const GitLabCommitSchema = z.object({
Expand Down Expand Up @@ -1033,3 +1043,5 @@ export type CreateWikiPageOptions = z.infer<typeof CreateWikiPageSchema>;
export type UpdateWikiPageOptions = z.infer<typeof UpdateWikiPageSchema>;
export type DeleteWikiPageOptions = z.infer<typeof DeleteWikiPageSchema>;
export type GitLabWikiPage = z.infer<typeof GitLabWikiPageSchema>;
export type GitLabTreeItem = z.infer<typeof GitLabTreeItemSchema>;
export type GetRepositoryTreeOptions = z.infer<typeof GetRepositoryTreeSchema>;