Add code
This commit is contained in:
489
src/router.ts
Normal file
489
src/router.ts
Normal 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;
|
||||
Reference in New Issue
Block a user