This commit is contained in:
2026-05-19 16:45:59 +02:00
parent 29496cb944
commit 992f25a9a2
16 changed files with 6222 additions and 116 deletions

489
src/router.ts Normal file
View File

@@ -0,0 +1,489 @@
/**
* MCP tool router that maps tool calls to Bitbucket API operations.
*/
import { BitbucketClient } from './bitbucket-client.js';
import { DefaultConfigLoader } from './config.js';
interface ToolCallParams {
[key: string]: any;
}
export interface ToolResult {
success: boolean;
data?: any;
error?: string;
}
/**
* Router that maps MCP tool calls to Bitbucket API operations.
*/
export class BitbucketRouter {
private client: BitbucketClient;
constructor() {
this.client = new BitbucketClient();
}
private getDefaultParams(params: ToolCallParams): { workspace: string | undefined; repoSlug: string | undefined } {
const defaults = DefaultConfigLoader.load();
return {
workspace: params.workspace || defaults.workspace || undefined,
repoSlug: params.repository || params.repo || defaults.repo || undefined
};
}
/**
* Validate the Bitbucket token.
*/
private async validateToken(): Promise<ToolResult> {
try {
const result = await this.client.validateToken();
return {
success: result.valid,
data: result
};
} catch (error) {
return {
success: false,
error: `Token validation failed: ${String(error)}`
};
}
}
/**
* Execute a tool call and return MCP-compatible result.
*/
async executeTool(
toolName: string,
params: ToolCallParams
): Promise<ToolResult> {
switch (toolName) {
case 'validate_token':
return this.validateToken();
case 'list_pull_requests':
return this.listPullRequests(params);
case 'get_pull_request':
return this.getPullRequest(params);
case 'get_pull_request_activities':
return this.getPullRequestActivities(params);
case 'get_pull_request_changes':
return this.getPullRequestChanges(params);
case 'get_pull_request_comments':
return this.getPullRequestComments(params);
case 'get_pull_request_comment':
return this.getPullRequestComment(params);
case 'get_pull_request_commits':
return this.getPullRequestCommits(params);
case 'get_pull_request_diff':
return this.getPullRequestDiff(params);
case 'get_pull_request_patch':
return this.getPullRequestPatch(params);
case 'get_pull_request_participants':
return this.getPullRequestParticipants(params);
case 'get_pull_request_reviewers':
return this.getPullRequestReviewers(params);
case 'get_pull_request_status':
return this.getPullRequestStatus(params);
case 'get_pull_request_tasks':
return this.getPullRequestTasks(params);
case 'get_pull_request_task_count':
return this.getPullRequestTaskCount(params);
case 'get_full_pull_request':
return this.getFullPullRequest(params);
default:
return {
success: false,
error: `Unknown tool: ${toolName}`
};
}
}
/**
* List pull requests in a repository.
*/
private async listPullRequests(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
if (!workspace || !repoSlug) {
return {
success: false,
error: 'Missing required parameters: workspace and repository'
};
}
// Default to open PRs if state not specified
const options = params.state ? { state: params.state } : undefined;
const prs = await this.client.listPullRequests(workspace, repoSlug, options);
return {
success: true,
data: {
pull_requests: prs,
count: prs.length
}
};
} catch (error) {
return {
success: false,
error: `List pull requests failed: ${typeof error === 'string' ? error : String(error)}`
};
}
}
/**
* Get a specific pull request.
*/
private async getPullRequest(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return {
success: false,
error: 'Missing required parameters'
};
}
const pr = await this.client.getPullRequest(workspace, repoSlug, prId);
return {
success: true,
data: pr
};
} catch (error) {
return {
success: false,
error: `Get pull request failed: ${typeof error === 'string' ? error : String(error)}`
};
}
}
/**
* Get pull request activities (events/actions).
*/
private async getPullRequestActivities(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestActivities(workspace, repoSlug, prId, {
limit: params.limit,
start: params.start
});
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get activities failed: ${String(error)}` };
}
}
/**
* Get pull request changes (files modified).
*/
private async getPullRequestChanges(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestChanges(workspace, repoSlug, prId, {
limit: params.limit,
start: params.start
});
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get changes failed: ${String(error)}` };
}
}
/**
* Get pull request comments.
*/
private async getPullRequestComments(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestComments(workspace, repoSlug, prId, {
limit: params.limit,
start: params.start
});
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get comments failed: ${String(error)}` };
}
}
/**
* Get a specific pull request comment.
*/
private async getPullRequestComment(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
const commentId = parseInt(params.commentId || params.comment_id, 10);
if (!workspace || !repoSlug || isNaN(prId) || isNaN(commentId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestComment(workspace, repoSlug, prId, commentId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get comment failed: ${String(error)}` };
}
}
/**
* Get commits in a pull request.
*/
private async getPullRequestCommits(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestCommits(workspace, repoSlug, prId, {
limit: params.limit,
start: params.start
});
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get commits failed: ${String(error)}` };
}
}
/**
* Get pull request diff.
*/
private async getPullRequestDiff(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestDiff(workspace, repoSlug, prId, {
context: params.context,
path: params.path,
whitespace: params.whitespace
});
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get diff failed: ${String(error)}` };
}
}
/**
* Get pull request patch.
*/
private async getPullRequestPatch(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestPatch(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get patch failed: ${String(error)}` };
}
}
/**
* Get pull request participants.
*/
private async getPullRequestParticipants(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestParticipants(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get participants failed: ${String(error)}` };
}
}
/**
* Get pull request reviewers.
*/
private async getPullRequestReviewers(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestReviewers(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get reviewers failed: ${String(error)}` };
}
}
/**
* Get pull request status.
*/
private async getPullRequestStatus(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestStatus(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get status failed: ${String(error)}` };
}
}
/**
* Get pull request tasks.
*/
private async getPullRequestTasks(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestTasks(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get tasks failed: ${String(error)}` };
}
}
/**
* Get pull request task count.
*/
private async getPullRequestTaskCount(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getPullRequestTaskCount(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get task count failed: ${String(error)}` };
}
}
/**
* Get full pull request details.
*/
private async getFullPullRequest(
params: ToolCallParams
): Promise<ToolResult> {
try {
const { workspace, repoSlug } = this.getDefaultParams(params);
const prId = parseInt(params.pullRequestId || params.pr_id, 10);
if (!workspace || !repoSlug || isNaN(prId)) {
return { success: false, error: 'Missing required parameters' };
}
const result = await this.client.getFullPullRequest(workspace, repoSlug, prId);
return { success: true, data: result };
} catch (error) {
return { success: false, error: `Get full PR failed: ${String(error)}` };
}
}
}
export default BitbucketRouter;