@lexmata/bitbucket-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +281 -0
- package/dist/api/branches.js +27 -0
- package/dist/api/branches.js.map +1 -0
- package/dist/api/client.js +88 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/commits.js +32 -0
- package/dist/api/commits.js.map +1 -0
- package/dist/api/issues.js +51 -0
- package/dist/api/issues.js.map +1 -0
- package/dist/api/pipelines.js +34 -0
- package/dist/api/pipelines.js.map +1 -0
- package/dist/api/pullrequests.js +91 -0
- package/dist/api/pullrequests.js.map +1 -0
- package/dist/api/repositories.js +36 -0
- package/dist/api/repositories.js.map +1 -0
- package/dist/api/search.js +15 -0
- package/dist/api/search.js.map +1 -0
- package/dist/auth/oauth.js +104 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.js +122 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/tools/index.js +1698 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export class PullRequestsAPI {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client){
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
async list(params) {
|
|
7
|
+
const { workspace, repo_slug, ...queryParams } = params;
|
|
8
|
+
return this.client.get(`/repositories/${workspace}/${repo_slug}/pullrequests`, queryParams);
|
|
9
|
+
}
|
|
10
|
+
async get(workspace, repo_slug, pr_id) {
|
|
11
|
+
return this.client.get(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}`);
|
|
12
|
+
}
|
|
13
|
+
async create(params) {
|
|
14
|
+
const { workspace, repo_slug, title, source_branch, destination_branch, description, close_source_branch, reviewers } = params;
|
|
15
|
+
const body = {
|
|
16
|
+
title,
|
|
17
|
+
source: {
|
|
18
|
+
branch: {
|
|
19
|
+
name: source_branch
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
close_source_branch: close_source_branch ?? false
|
|
23
|
+
};
|
|
24
|
+
if (destination_branch) {
|
|
25
|
+
body.destination = {
|
|
26
|
+
branch: {
|
|
27
|
+
name: destination_branch
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (description) {
|
|
32
|
+
body.description = description;
|
|
33
|
+
}
|
|
34
|
+
if (reviewers && reviewers.length > 0) {
|
|
35
|
+
body.reviewers = reviewers.map((uuid)=>({
|
|
36
|
+
uuid
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
return this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests`, body);
|
|
40
|
+
}
|
|
41
|
+
async update(workspace, repo_slug, pr_id, updates) {
|
|
42
|
+
const body = {};
|
|
43
|
+
if (updates.title) body.title = updates.title;
|
|
44
|
+
if (updates.description) body.description = updates.description;
|
|
45
|
+
if (updates.destination_branch) {
|
|
46
|
+
body.destination = {
|
|
47
|
+
branch: {
|
|
48
|
+
name: updates.destination_branch
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return this.client.put(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}`, body);
|
|
53
|
+
}
|
|
54
|
+
async merge(workspace, repo_slug, pr_id, options) {
|
|
55
|
+
return this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/merge`, options);
|
|
56
|
+
}
|
|
57
|
+
async decline(workspace, repo_slug, pr_id) {
|
|
58
|
+
return this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/decline`);
|
|
59
|
+
}
|
|
60
|
+
async approve(workspace, repo_slug, pr_id) {
|
|
61
|
+
await this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/approve`);
|
|
62
|
+
}
|
|
63
|
+
async unapprove(workspace, repo_slug, pr_id) {
|
|
64
|
+
await this.client.delete(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/approve`);
|
|
65
|
+
}
|
|
66
|
+
async requestChanges(workspace, repo_slug, pr_id) {
|
|
67
|
+
await this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/request-changes`);
|
|
68
|
+
}
|
|
69
|
+
async listComments(workspace, repo_slug, pr_id, params) {
|
|
70
|
+
return this.client.get(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/comments`, params);
|
|
71
|
+
}
|
|
72
|
+
async addComment(workspace, repo_slug, pr_id, content, inline) {
|
|
73
|
+
const body = {
|
|
74
|
+
content: {
|
|
75
|
+
raw: content
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
if (inline) {
|
|
79
|
+
body.inline = {
|
|
80
|
+
path: inline.path,
|
|
81
|
+
to: inline.line
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/comments`, body);
|
|
85
|
+
}
|
|
86
|
+
async getDiff(workspace, repo_slug, pr_id) {
|
|
87
|
+
return this.client.getRaw(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/diff`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=pullrequests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/pullrequests.ts"],"sourcesContent":["import type { BitbucketClient } from './client.js';\nimport type {\n BitbucketPullRequest,\n BitbucketPRComment,\n PaginatedResponse,\n ListPullRequestsParams,\n CreatePullRequestParams,\n} from '../types/index.js';\n\nexport class PullRequestsAPI {\n constructor(private client: BitbucketClient) {}\n\n /**\n * List pull requests for a repository\n */\n async list(params: ListPullRequestsParams): Promise<PaginatedResponse<BitbucketPullRequest>> {\n const { workspace, repo_slug, ...queryParams } = params;\n return this.client.get<PaginatedResponse<BitbucketPullRequest>>(\n `/repositories/${workspace}/${repo_slug}/pullrequests`,\n queryParams as Record<string, string | number | undefined>\n );\n }\n\n /**\n * Get a specific pull request\n */\n async get(workspace: string, repo_slug: string, pr_id: number): Promise<BitbucketPullRequest> {\n return this.client.get<BitbucketPullRequest>(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}`\n );\n }\n\n /**\n * Create a new pull request\n */\n async create(params: CreatePullRequestParams): Promise<BitbucketPullRequest> {\n const {\n workspace,\n repo_slug,\n title,\n source_branch,\n destination_branch,\n description,\n close_source_branch,\n reviewers,\n } = params;\n\n const body: Record<string, unknown> = {\n title,\n source: {\n branch: { name: source_branch },\n },\n close_source_branch: close_source_branch ?? false,\n };\n\n if (destination_branch) {\n body.destination = { branch: { name: destination_branch } };\n }\n\n if (description) {\n body.description = description;\n }\n\n if (reviewers && reviewers.length > 0) {\n body.reviewers = reviewers.map((uuid) => ({ uuid }));\n }\n\n return this.client.post<BitbucketPullRequest>(\n `/repositories/${workspace}/${repo_slug}/pullrequests`,\n body\n );\n }\n\n /**\n * Update a pull request\n */\n async update(\n workspace: string,\n repo_slug: string,\n pr_id: number,\n updates: { title?: string; description?: string; destination_branch?: string }\n ): Promise<BitbucketPullRequest> {\n const body: Record<string, unknown> = {};\n\n if (updates.title) body.title = updates.title;\n if (updates.description) body.description = updates.description;\n if (updates.destination_branch) {\n body.destination = { branch: { name: updates.destination_branch } };\n }\n\n return this.client.put<BitbucketPullRequest>(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}`,\n body\n );\n }\n\n /**\n * Merge a pull request\n */\n async merge(\n workspace: string,\n repo_slug: string,\n pr_id: number,\n options?: {\n message?: string;\n close_source_branch?: boolean;\n merge_strategy?: 'merge_commit' | 'squash' | 'fast_forward';\n }\n ): Promise<BitbucketPullRequest> {\n return this.client.post<BitbucketPullRequest>(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/merge`,\n options\n );\n }\n\n /**\n * Decline a pull request\n */\n async decline(\n workspace: string,\n repo_slug: string,\n pr_id: number\n ): Promise<BitbucketPullRequest> {\n return this.client.post<BitbucketPullRequest>(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/decline`\n );\n }\n\n /**\n * Approve a pull request\n */\n async approve(workspace: string, repo_slug: string, pr_id: number): Promise<void> {\n await this.client.post(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/approve`);\n }\n\n /**\n * Unapprove a pull request\n */\n async unapprove(workspace: string, repo_slug: string, pr_id: number): Promise<void> {\n await this.client.delete(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/approve`\n );\n }\n\n /**\n * Request changes on a pull request\n */\n async requestChanges(workspace: string, repo_slug: string, pr_id: number): Promise<void> {\n await this.client.post(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/request-changes`\n );\n }\n\n /**\n * List comments on a pull request\n */\n async listComments(\n workspace: string,\n repo_slug: string,\n pr_id: number,\n params?: { page?: number; pagelen?: number }\n ): Promise<PaginatedResponse<BitbucketPRComment>> {\n return this.client.get<PaginatedResponse<BitbucketPRComment>>(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/comments`,\n params as Record<string, string | number | undefined>\n );\n }\n\n /**\n * Add a comment to a pull request\n */\n async addComment(\n workspace: string,\n repo_slug: string,\n pr_id: number,\n content: string,\n inline?: { path: string; line?: number }\n ): Promise<BitbucketPRComment> {\n const body: Record<string, unknown> = {\n content: { raw: content },\n };\n\n if (inline) {\n body.inline = {\n path: inline.path,\n to: inline.line,\n };\n }\n\n return this.client.post<BitbucketPRComment>(\n `/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/comments`,\n body\n );\n }\n\n /**\n * Get diff for a pull request\n */\n async getDiff(workspace: string, repo_slug: string, pr_id: number): Promise<string> {\n return this.client.getRaw(`/repositories/${workspace}/${repo_slug}/pullrequests/${pr_id}/diff`);\n }\n}\n"],"names":["PullRequestsAPI","client","list","params","workspace","repo_slug","queryParams","get","pr_id","create","title","source_branch","destination_branch","description","close_source_branch","reviewers","body","source","branch","name","destination","length","map","uuid","post","update","updates","put","merge","options","decline","approve","unapprove","delete","requestChanges","listComments","addComment","content","inline","raw","path","to","line","getDiff","getRaw"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class RepositoriesAPI {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client){
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
async list(params) {
|
|
7
|
+
const { workspace, ...queryParams } = params;
|
|
8
|
+
return this.client.get(`/repositories/${workspace}`, queryParams);
|
|
9
|
+
}
|
|
10
|
+
async get(params) {
|
|
11
|
+
const { workspace, repo_slug } = params;
|
|
12
|
+
return this.client.get(`/repositories/${workspace}/${repo_slug}`);
|
|
13
|
+
}
|
|
14
|
+
async create(params) {
|
|
15
|
+
const { workspace, repo_slug, ...body } = params;
|
|
16
|
+
return this.client.post(`/repositories/${workspace}/${repo_slug}`, body);
|
|
17
|
+
}
|
|
18
|
+
async delete(params) {
|
|
19
|
+
const { workspace, repo_slug } = params;
|
|
20
|
+
await this.client.delete(`/repositories/${workspace}/${repo_slug}`);
|
|
21
|
+
}
|
|
22
|
+
async listForks(params) {
|
|
23
|
+
const { workspace, repo_slug, ...queryParams } = params;
|
|
24
|
+
return this.client.get(`/repositories/${workspace}/${repo_slug}/forks`, queryParams);
|
|
25
|
+
}
|
|
26
|
+
async getFileContent(workspace, repo_slug, path, ref) {
|
|
27
|
+
const endpoint = ref ? `/repositories/${workspace}/${repo_slug}/src/${ref}/${path}` : `/repositories/${workspace}/${repo_slug}/src/HEAD/${path}`;
|
|
28
|
+
return this.client.getRaw(endpoint);
|
|
29
|
+
}
|
|
30
|
+
async listFiles(workspace, repo_slug, path = '', ref) {
|
|
31
|
+
const endpoint = ref ? `/repositories/${workspace}/${repo_slug}/src/${ref}/${path}` : `/repositories/${workspace}/${repo_slug}/src/HEAD/${path}`;
|
|
32
|
+
return this.client.get(endpoint);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=repositories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/repositories.ts"],"sourcesContent":["import type { BitbucketClient } from './client.js';\nimport type {\n BitbucketRepository,\n PaginatedResponse,\n ListRepositoriesParams,\n GetRepositoryParams,\n CreateRepositoryParams,\n} from '../types/index.js';\n\nexport class RepositoriesAPI {\n constructor(private client: BitbucketClient) {}\n\n /**\n * List repositories for a workspace\n */\n async list(params: ListRepositoriesParams): Promise<PaginatedResponse<BitbucketRepository>> {\n const { workspace, ...queryParams } = params;\n return this.client.get<PaginatedResponse<BitbucketRepository>>(\n `/repositories/${workspace}`,\n queryParams as Record<string, string | number | undefined>\n );\n }\n\n /**\n * Get a specific repository\n */\n async get(params: GetRepositoryParams): Promise<BitbucketRepository> {\n const { workspace, repo_slug } = params;\n return this.client.get<BitbucketRepository>(`/repositories/${workspace}/${repo_slug}`);\n }\n\n /**\n * Create a new repository\n */\n async create(params: CreateRepositoryParams): Promise<BitbucketRepository> {\n const { workspace, repo_slug, ...body } = params;\n return this.client.post<BitbucketRepository>(`/repositories/${workspace}/${repo_slug}`, body);\n }\n\n /**\n * Delete a repository\n */\n async delete(params: GetRepositoryParams): Promise<void> {\n const { workspace, repo_slug } = params;\n await this.client.delete(`/repositories/${workspace}/${repo_slug}`);\n }\n\n /**\n * List repository forks\n */\n async listForks(\n params: GetRepositoryParams & { page?: number; pagelen?: number }\n ): Promise<PaginatedResponse<BitbucketRepository>> {\n const { workspace, repo_slug, ...queryParams } = params;\n return this.client.get<PaginatedResponse<BitbucketRepository>>(\n `/repositories/${workspace}/${repo_slug}/forks`,\n queryParams as Record<string, string | number | undefined>\n );\n }\n\n /**\n * Get repository file content\n */\n async getFileContent(\n workspace: string,\n repo_slug: string,\n path: string,\n ref?: string\n ): Promise<string> {\n const endpoint = ref\n ? `/repositories/${workspace}/${repo_slug}/src/${ref}/${path}`\n : `/repositories/${workspace}/${repo_slug}/src/HEAD/${path}`;\n return this.client.getRaw(endpoint);\n }\n\n /**\n * List files in a directory\n */\n async listFiles(\n workspace: string,\n repo_slug: string,\n path: string = '',\n ref?: string\n ): Promise<PaginatedResponse<{ path: string; type: string }>> {\n const endpoint = ref\n ? `/repositories/${workspace}/${repo_slug}/src/${ref}/${path}`\n : `/repositories/${workspace}/${repo_slug}/src/HEAD/${path}`;\n return this.client.get(endpoint);\n }\n}\n"],"names":["RepositoriesAPI","client","list","params","workspace","queryParams","get","repo_slug","create","body","post","delete","listForks","getFileContent","path","ref","endpoint","getRaw","listFiles"],"mappings":"AASA,OAAO,MAAMA;;IACX,YAAY,AAAQC,MAAuB,CAAE;aAAzBA,SAAAA;IAA0B;IAK9C,MAAMC,KAAKC,MAA8B,EAAmD;QAC1F,MAAM,EAAEC,SAAS,EAAE,GAAGC,aAAa,GAAGF;QACtC,OAAO,IAAI,CAACF,MAAM,CAACK,GAAG,CACpB,CAAC,cAAc,EAAEF,WAAW,EAC5BC;IAEJ;IAKA,MAAMC,IAAIH,MAA2B,EAAgC;QACnE,MAAM,EAAEC,SAAS,EAAEG,SAAS,EAAE,GAAGJ;QACjC,OAAO,IAAI,CAACF,MAAM,CAACK,GAAG,CAAsB,CAAC,cAAc,EAAEF,UAAU,CAAC,EAAEG,WAAW;IACvF;IAKA,MAAMC,OAAOL,MAA8B,EAAgC;QACzE,MAAM,EAAEC,SAAS,EAAEG,SAAS,EAAE,GAAGE,MAAM,GAAGN;QAC1C,OAAO,IAAI,CAACF,MAAM,CAACS,IAAI,CAAsB,CAAC,cAAc,EAAEN,UAAU,CAAC,EAAEG,WAAW,EAAEE;IAC1F;IAKA,MAAME,OAAOR,MAA2B,EAAiB;QACvD,MAAM,EAAEC,SAAS,EAAEG,SAAS,EAAE,GAAGJ;QACjC,MAAM,IAAI,CAACF,MAAM,CAACU,MAAM,CAAC,CAAC,cAAc,EAAEP,UAAU,CAAC,EAAEG,WAAW;IACpE;IAKA,MAAMK,UACJT,MAAiE,EAChB;QACjD,MAAM,EAAEC,SAAS,EAAEG,SAAS,EAAE,GAAGF,aAAa,GAAGF;QACjD,OAAO,IAAI,CAACF,MAAM,CAACK,GAAG,CACpB,CAAC,cAAc,EAAEF,UAAU,CAAC,EAAEG,UAAU,MAAM,CAAC,EAC/CF;IAEJ;IAKA,MAAMQ,eACJT,SAAiB,EACjBG,SAAiB,EACjBO,IAAY,EACZC,GAAY,EACK;QACjB,MAAMC,WAAWD,MACb,CAAC,cAAc,EAAEX,UAAU,CAAC,EAAEG,UAAU,KAAK,EAAEQ,IAAI,CAAC,EAAED,MAAM,GAC5D,CAAC,cAAc,EAAEV,UAAU,CAAC,EAAEG,UAAU,UAAU,EAAEO,MAAM;QAC9D,OAAO,IAAI,CAACb,MAAM,CAACgB,MAAM,CAACD;IAC5B;IAKA,MAAME,UACJd,SAAiB,EACjBG,SAAiB,EACjBO,OAAe,EAAE,EACjBC,GAAY,EACgD;QAC5D,MAAMC,WAAWD,MACb,CAAC,cAAc,EAAEX,UAAU,CAAC,EAAEG,UAAU,KAAK,EAAEQ,IAAI,CAAC,EAAED,MAAM,GAC5D,CAAC,cAAc,EAAEV,UAAU,CAAC,EAAEG,UAAU,UAAU,EAAEO,MAAM;QAC9D,OAAO,IAAI,CAACb,MAAM,CAACK,GAAG,CAACU;IACzB;AACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class SearchAPI {
|
|
2
|
+
client;
|
|
3
|
+
constructor(client){
|
|
4
|
+
this.client = client;
|
|
5
|
+
}
|
|
6
|
+
async searchCode(params) {
|
|
7
|
+
const { workspace, search_query, ...queryParams } = params;
|
|
8
|
+
return this.client.get(`/workspaces/${workspace}/search/code`, {
|
|
9
|
+
search_query,
|
|
10
|
+
...queryParams
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/search.ts"],"sourcesContent":["import type { BitbucketClient } from './client.js';\nimport type { BitbucketSearchResult, PaginatedResponse, SearchCodeParams } from '../types/index.js';\n\nexport class SearchAPI {\n constructor(private client: BitbucketClient) {}\n\n /**\n * Search code in a workspace\n */\n async searchCode(params: SearchCodeParams): Promise<PaginatedResponse<BitbucketSearchResult>> {\n const { workspace, search_query, ...queryParams } = params;\n return this.client.get<PaginatedResponse<BitbucketSearchResult>>(\n `/workspaces/${workspace}/search/code`,\n {\n search_query,\n ...queryParams,\n } as Record<string, string | number | undefined>\n );\n }\n}\n"],"names":["SearchAPI","client","searchCode","params","workspace","search_query","queryParams","get"],"mappings":"AAGA,OAAO,MAAMA;;IACX,YAAY,AAAQC,MAAuB,CAAE;aAAzBA,SAAAA;IAA0B;IAK9C,MAAMC,WAAWC,MAAwB,EAAqD;QAC5F,MAAM,EAAEC,SAAS,EAAEC,YAAY,EAAE,GAAGC,aAAa,GAAGH;QACpD,OAAO,IAAI,CAACF,MAAM,CAACM,GAAG,CACpB,CAAC,YAAY,EAAEH,UAAU,YAAY,CAAC,EACtC;YACEC;YACA,GAAGC,WAAW;QAChB;IAEJ;AACF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const BITBUCKET_AUTH_URL = 'https://bitbucket.org/site/oauth2/authorize';
|
|
2
|
+
const BITBUCKET_TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token';
|
|
3
|
+
export class BitbucketOAuth {
|
|
4
|
+
config;
|
|
5
|
+
tokenState = null;
|
|
6
|
+
constructor(config){
|
|
7
|
+
this.config = config;
|
|
8
|
+
if (config.accessToken) {
|
|
9
|
+
this.tokenState = {
|
|
10
|
+
accessToken: config.accessToken,
|
|
11
|
+
refreshToken: config.refreshToken ?? '',
|
|
12
|
+
expiresAt: Date.now() + 3600 * 1000
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
getAuthorizationUrl(scopes = [
|
|
17
|
+
'repository',
|
|
18
|
+
'pullrequest',
|
|
19
|
+
'issue',
|
|
20
|
+
'pipeline'
|
|
21
|
+
]) {
|
|
22
|
+
const params = new URLSearchParams({
|
|
23
|
+
client_id: this.config.clientId,
|
|
24
|
+
response_type: 'code',
|
|
25
|
+
scope: scopes.join(' ')
|
|
26
|
+
});
|
|
27
|
+
return `${BITBUCKET_AUTH_URL}?${params.toString()}`;
|
|
28
|
+
}
|
|
29
|
+
async exchangeCodeForToken(code) {
|
|
30
|
+
const response = await fetch(BITBUCKET_TOKEN_URL, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
34
|
+
Authorization: `Basic ${Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64')}`
|
|
35
|
+
},
|
|
36
|
+
body: new URLSearchParams({
|
|
37
|
+
grant_type: 'authorization_code',
|
|
38
|
+
code
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
const error = await response.text();
|
|
43
|
+
throw new Error(`Failed to exchange code for token: ${error}`);
|
|
44
|
+
}
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
this.tokenState = {
|
|
47
|
+
accessToken: data.access_token,
|
|
48
|
+
refreshToken: data.refresh_token,
|
|
49
|
+
expiresAt: Date.now() + data.expires_in * 1000
|
|
50
|
+
};
|
|
51
|
+
return this.tokenState;
|
|
52
|
+
}
|
|
53
|
+
async refreshAccessToken() {
|
|
54
|
+
if (!this.tokenState?.refreshToken) {
|
|
55
|
+
throw new Error('No refresh token available');
|
|
56
|
+
}
|
|
57
|
+
const response = await fetch(BITBUCKET_TOKEN_URL, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
|
+
Authorization: `Basic ${Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64')}`
|
|
62
|
+
},
|
|
63
|
+
body: new URLSearchParams({
|
|
64
|
+
grant_type: 'refresh_token',
|
|
65
|
+
refresh_token: this.tokenState.refreshToken
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const error = await response.text();
|
|
70
|
+
throw new Error(`Failed to refresh token: ${error}`);
|
|
71
|
+
}
|
|
72
|
+
const data = await response.json();
|
|
73
|
+
this.tokenState = {
|
|
74
|
+
accessToken: data.access_token,
|
|
75
|
+
refreshToken: data.refresh_token,
|
|
76
|
+
expiresAt: Date.now() + data.expires_in * 1000
|
|
77
|
+
};
|
|
78
|
+
return this.tokenState;
|
|
79
|
+
}
|
|
80
|
+
async getAccessToken() {
|
|
81
|
+
if (!this.tokenState) {
|
|
82
|
+
throw new Error('Not authenticated. Please complete OAuth flow first.');
|
|
83
|
+
}
|
|
84
|
+
const expiresIn = this.tokenState.expiresAt - Date.now();
|
|
85
|
+
if (expiresIn < 5 * 60 * 1000 && this.tokenState.refreshToken) {
|
|
86
|
+
await this.refreshAccessToken();
|
|
87
|
+
}
|
|
88
|
+
return this.tokenState.accessToken;
|
|
89
|
+
}
|
|
90
|
+
isAuthenticated() {
|
|
91
|
+
return this.tokenState !== null;
|
|
92
|
+
}
|
|
93
|
+
setTokenState(state) {
|
|
94
|
+
this.tokenState = state;
|
|
95
|
+
}
|
|
96
|
+
getTokenState() {
|
|
97
|
+
return this.tokenState;
|
|
98
|
+
}
|
|
99
|
+
clearAuth() {
|
|
100
|
+
this.tokenState = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/oauth.ts"],"sourcesContent":["import type { OAuthConfig, OAuthTokenResponse, TokenState } from '../types/index.js';\n\nconst BITBUCKET_AUTH_URL = 'https://bitbucket.org/site/oauth2/authorize';\nconst BITBUCKET_TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token';\n\nexport class BitbucketOAuth {\n private config: OAuthConfig;\n private tokenState: TokenState | null = null;\n\n constructor(config: OAuthConfig) {\n this.config = config;\n\n // Initialize with pre-configured tokens if provided\n if (config.accessToken) {\n this.tokenState = {\n accessToken: config.accessToken,\n refreshToken: config.refreshToken ?? '',\n expiresAt: Date.now() + 3600 * 1000, // Assume 1 hour if not known\n };\n }\n }\n\n /**\n * Generate the authorization URL for OAuth flow\n */\n getAuthorizationUrl(\n scopes: string[] = ['repository', 'pullrequest', 'issue', 'pipeline']\n ): string {\n const params = new URLSearchParams({\n client_id: this.config.clientId,\n response_type: 'code',\n scope: scopes.join(' '),\n });\n\n return `${BITBUCKET_AUTH_URL}?${params.toString()}`;\n }\n\n /**\n * Exchange authorization code for access token\n */\n async exchangeCodeForToken(code: string): Promise<TokenState> {\n const response = await fetch(BITBUCKET_TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Basic ${Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64')}`,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to exchange code for token: ${error}`);\n }\n\n const data = (await response.json()) as OAuthTokenResponse;\n\n this.tokenState = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n };\n\n return this.tokenState;\n }\n\n /**\n * Refresh the access token using refresh token\n */\n async refreshAccessToken(): Promise<TokenState> {\n if (!this.tokenState?.refreshToken) {\n throw new Error('No refresh token available');\n }\n\n const response = await fetch(BITBUCKET_TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Basic ${Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64')}`,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: this.tokenState.refreshToken,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to refresh token: ${error}`);\n }\n\n const data = (await response.json()) as OAuthTokenResponse;\n\n this.tokenState = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: Date.now() + data.expires_in * 1000,\n };\n\n return this.tokenState;\n }\n\n /**\n * Get a valid access token, refreshing if necessary\n */\n async getAccessToken(): Promise<string> {\n if (!this.tokenState) {\n throw new Error('Not authenticated. Please complete OAuth flow first.');\n }\n\n // Refresh if token expires in less than 5 minutes\n const expiresIn = this.tokenState.expiresAt - Date.now();\n if (expiresIn < 5 * 60 * 1000 && this.tokenState.refreshToken) {\n await this.refreshAccessToken();\n }\n\n return this.tokenState.accessToken;\n }\n\n /**\n * Check if currently authenticated\n */\n isAuthenticated(): boolean {\n return this.tokenState !== null;\n }\n\n /**\n * Set token state directly (for restoration from storage)\n */\n setTokenState(state: TokenState): void {\n this.tokenState = state;\n }\n\n /**\n * Get current token state (for persistence)\n */\n getTokenState(): TokenState | null {\n return this.tokenState;\n }\n\n /**\n * Clear authentication state\n */\n clearAuth(): void {\n this.tokenState = null;\n }\n}\n"],"names":["BITBUCKET_AUTH_URL","BITBUCKET_TOKEN_URL","BitbucketOAuth","config","tokenState","accessToken","refreshToken","expiresAt","Date","now","getAuthorizationUrl","scopes","params","URLSearchParams","client_id","clientId","response_type","scope","join","toString","exchangeCodeForToken","code","response","fetch","method","headers","Authorization","Buffer","from","clientSecret","body","grant_type","ok","error","text","Error","data","json","access_token","refresh_token","expires_in","refreshAccessToken","getAccessToken","expiresIn","isAuthenticated","setTokenState","state","getTokenState","clearAuth"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { BitbucketOAuth } from './auth/oauth.js';
|
|
6
|
+
import { BitbucketClient } from './api/client.js';
|
|
7
|
+
import { ToolHandler, toolDefinitions } from './tools/index.js';
|
|
8
|
+
import { ResourceHandler } from './resources/index.js';
|
|
9
|
+
const config = {
|
|
10
|
+
clientId: process.env.BITBUCKET_CLIENT_ID ?? '',
|
|
11
|
+
clientSecret: process.env.BITBUCKET_CLIENT_SECRET ?? '',
|
|
12
|
+
accessToken: process.env.BITBUCKET_ACCESS_TOKEN,
|
|
13
|
+
refreshToken: process.env.BITBUCKET_REFRESH_TOKEN
|
|
14
|
+
};
|
|
15
|
+
function validateConfig() {
|
|
16
|
+
if (config.accessToken) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!config.clientId || !config.clientSecret) {
|
|
20
|
+
console.error('Error: Missing required configuration.');
|
|
21
|
+
console.error('');
|
|
22
|
+
console.error('Please set one of the following:');
|
|
23
|
+
console.error(' 1. BITBUCKET_ACCESS_TOKEN (for direct token auth)');
|
|
24
|
+
console.error(' 2. BITBUCKET_CLIENT_ID and BITBUCKET_CLIENT_SECRET (for OAuth)');
|
|
25
|
+
console.error('');
|
|
26
|
+
console.error('See README.md for setup instructions.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function main() {
|
|
31
|
+
validateConfig();
|
|
32
|
+
const oauth = new BitbucketOAuth(config);
|
|
33
|
+
const client = new BitbucketClient(oauth);
|
|
34
|
+
const toolHandler = new ToolHandler(client);
|
|
35
|
+
const resourceHandler = new ResourceHandler(client);
|
|
36
|
+
const server = new Server({
|
|
37
|
+
name: 'bitbucket-mcp',
|
|
38
|
+
version: '1.0.0'
|
|
39
|
+
}, {
|
|
40
|
+
capabilities: {
|
|
41
|
+
tools: {},
|
|
42
|
+
resources: {}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
server.setRequestHandler(ListToolsRequestSchema, ()=>{
|
|
46
|
+
return Promise.resolve({
|
|
47
|
+
tools: toolDefinitions
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
server.setRequestHandler(CallToolRequestSchema, async (request)=>{
|
|
51
|
+
const { name, arguments: args } = request.params;
|
|
52
|
+
try {
|
|
53
|
+
const result = await toolHandler.handleTool(name, args ?? {});
|
|
54
|
+
return {
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: JSON.stringify(result, null, 2)
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: `Error: ${message}`
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
isError: true
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, ()=>{
|
|
76
|
+
return Promise.resolve({
|
|
77
|
+
resourceTemplates: resourceHandler.listResourceTemplates()
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
server.setRequestHandler(ListResourcesRequestSchema, ()=>{
|
|
81
|
+
return Promise.resolve({
|
|
82
|
+
resources: []
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request)=>{
|
|
86
|
+
const { uri } = request.params;
|
|
87
|
+
try {
|
|
88
|
+
return await resourceHandler.readResource(uri);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
91
|
+
throw new Error(`Failed to read resource: ${message}`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const transport = new StdioServerTransport();
|
|
95
|
+
await server.connect(transport);
|
|
96
|
+
console.error('Bitbucket MCP server started');
|
|
97
|
+
}
|
|
98
|
+
main().catch((error)=>{
|
|
99
|
+
console.error('Fatal error:', error);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ReadResourceRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { BitbucketOAuth } from './auth/oauth.js';\nimport { BitbucketClient } from './api/client.js';\nimport { ToolHandler, toolDefinitions } from './tools/index.js';\nimport { ResourceHandler } from './resources/index.js';\n\n// Environment variable configuration\nconst config = {\n clientId: process.env.BITBUCKET_CLIENT_ID ?? '',\n clientSecret: process.env.BITBUCKET_CLIENT_SECRET ?? '',\n accessToken: process.env.BITBUCKET_ACCESS_TOKEN,\n refreshToken: process.env.BITBUCKET_REFRESH_TOKEN,\n};\n\n// Validate configuration\nfunction validateConfig(): void {\n // If access token is provided, we can work without OAuth credentials\n if (config.accessToken) {\n return;\n }\n\n // Otherwise, we need OAuth credentials\n if (!config.clientId || !config.clientSecret) {\n console.error('Error: Missing required configuration.');\n console.error('');\n console.error('Please set one of the following:');\n console.error(' 1. BITBUCKET_ACCESS_TOKEN (for direct token auth)');\n console.error(' 2. BITBUCKET_CLIENT_ID and BITBUCKET_CLIENT_SECRET (for OAuth)');\n console.error('');\n console.error('See README.md for setup instructions.');\n process.exit(1);\n }\n}\n\n// Create and start the MCP server\nasync function main(): Promise<void> {\n validateConfig();\n\n // Initialize OAuth and client\n const oauth = new BitbucketOAuth(config);\n const client = new BitbucketClient(oauth);\n const toolHandler = new ToolHandler(client);\n const resourceHandler = new ResourceHandler(client);\n\n // Create MCP server\n const server = new Server(\n {\n name: 'bitbucket-mcp',\n version: '1.0.0',\n },\n {\n capabilities: {\n tools: {},\n resources: {},\n },\n }\n );\n\n // Handle tool listing\n server.setRequestHandler(ListToolsRequestSchema, () => {\n return Promise.resolve({\n tools: toolDefinitions,\n });\n });\n\n // Handle tool calls\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n const result = await toolHandler.handleTool(name, args ?? {});\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${message}`,\n },\n ],\n isError: true,\n };\n }\n });\n\n // Handle resource template listing\n server.setRequestHandler(ListResourceTemplatesRequestSchema, () => {\n return Promise.resolve({\n resourceTemplates: resourceHandler.listResourceTemplates(),\n });\n });\n\n // Handle resource listing (empty since resources are dynamic)\n server.setRequestHandler(ListResourcesRequestSchema, () => {\n return Promise.resolve({\n resources: [],\n });\n });\n\n // Handle resource reading\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n const { uri } = request.params;\n\n try {\n return await resourceHandler.readResource(uri);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n throw new Error(`Failed to read resource: ${message}`);\n }\n });\n\n // Connect via stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n // Log to stderr so it doesn't interfere with MCP protocol\n console.error('Bitbucket MCP server started');\n}\n\n// Run the server\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n"],"names":["Server","StdioServerTransport","CallToolRequestSchema","ListToolsRequestSchema","ListResourcesRequestSchema","ListResourceTemplatesRequestSchema","ReadResourceRequestSchema","BitbucketOAuth","BitbucketClient","ToolHandler","toolDefinitions","ResourceHandler","config","clientId","process","env","BITBUCKET_CLIENT_ID","clientSecret","BITBUCKET_CLIENT_SECRET","accessToken","BITBUCKET_ACCESS_TOKEN","refreshToken","BITBUCKET_REFRESH_TOKEN","validateConfig","console","error","exit","main","oauth","client","toolHandler","resourceHandler","server","name","version","capabilities","tools","resources","setRequestHandler","Promise","resolve","request","arguments","args","params","result","handleTool","content","type","text","JSON","stringify","message","Error","isError","resourceTemplates","listResourceTemplates","uri","readResource","transport","connect","catch"],"mappings":";AAEA,SAASA,MAAM,QAAQ,4CAA4C;AACnE,SAASC,oBAAoB,QAAQ,4CAA4C;AACjF,SACEC,qBAAqB,EACrBC,sBAAsB,EACtBC,0BAA0B,EAC1BC,kCAAkC,EAClCC,yBAAyB,QACpB,qCAAqC;AAE5C,SAASC,cAAc,QAAQ,kBAAkB;AACjD,SAASC,eAAe,QAAQ,kBAAkB;AAClD,SAASC,WAAW,EAAEC,eAAe,QAAQ,mBAAmB;AAChE,SAASC,eAAe,QAAQ,uBAAuB;AAGvD,MAAMC,SAAS;IACbC,UAAUC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAC7CC,cAAcH,QAAQC,GAAG,CAACG,uBAAuB,IAAI;IACrDC,aAAaL,QAAQC,GAAG,CAACK,sBAAsB;IAC/CC,cAAcP,QAAQC,GAAG,CAACO,uBAAuB;AACnD;AAGA,SAASC;IAEP,IAAIX,OAAOO,WAAW,EAAE;QACtB;IACF;IAGA,IAAI,CAACP,OAAOC,QAAQ,IAAI,CAACD,OAAOK,YAAY,EAAE;QAC5CO,QAAQC,KAAK,CAAC;QACdD,QAAQC,KAAK,CAAC;QACdD,QAAQC,KAAK,CAAC;QACdD,QAAQC,KAAK,CAAC;QACdD,QAAQC,KAAK,CAAC;QACdD,QAAQC,KAAK,CAAC;QACdD,QAAQC,KAAK,CAAC;QACdX,QAAQY,IAAI,CAAC;IACf;AACF;AAGA,eAAeC;IACbJ;IAGA,MAAMK,QAAQ,IAAIrB,eAAeK;IACjC,MAAMiB,SAAS,IAAIrB,gBAAgBoB;IACnC,MAAME,cAAc,IAAIrB,YAAYoB;IACpC,MAAME,kBAAkB,IAAIpB,gBAAgBkB;IAG5C,MAAMG,SAAS,IAAIhC,OACjB;QACEiC,MAAM;QACNC,SAAS;IACX,GACA;QACEC,cAAc;YACZC,OAAO,CAAC;YACRC,WAAW,CAAC;QACd;IACF;IAIFL,OAAOM,iBAAiB,CAACnC,wBAAwB;QAC/C,OAAOoC,QAAQC,OAAO,CAAC;YACrBJ,OAAO1B;QACT;IACF;IAGAsB,OAAOM,iBAAiB,CAACpC,uBAAuB,OAAOuC;QACrD,MAAM,EAAER,IAAI,EAAES,WAAWC,IAAI,EAAE,GAAGF,QAAQG,MAAM;QAEhD,IAAI;YACF,MAAMC,SAAS,MAAMf,YAAYgB,UAAU,CAACb,MAAMU,QAAQ,CAAC;YAC3D,OAAO;gBACLI,SAAS;oBACP;wBACEC,MAAM;wBACNC,MAAMC,KAAKC,SAAS,CAACN,QAAQ,MAAM;oBACrC;iBACD;YACH;QACF,EAAE,OAAOpB,OAAO;YACd,MAAM2B,UAAU3B,iBAAiB4B,QAAQ5B,MAAM2B,OAAO,GAAG;YACzD,OAAO;gBACLL,SAAS;oBACP;wBACEC,MAAM;wBACNC,MAAM,CAAC,OAAO,EAAEG,SAAS;oBAC3B;iBACD;gBACDE,SAAS;YACX;QACF;IACF;IAGAtB,OAAOM,iBAAiB,CAACjC,oCAAoC;QAC3D,OAAOkC,QAAQC,OAAO,CAAC;YACrBe,mBAAmBxB,gBAAgByB,qBAAqB;QAC1D;IACF;IAGAxB,OAAOM,iBAAiB,CAAClC,4BAA4B;QACnD,OAAOmC,QAAQC,OAAO,CAAC;YACrBH,WAAW,EAAE;QACf;IACF;IAGAL,OAAOM,iBAAiB,CAAChC,2BAA2B,OAAOmC;QACzD,MAAM,EAAEgB,GAAG,EAAE,GAAGhB,QAAQG,MAAM;QAE9B,IAAI;YACF,OAAO,MAAMb,gBAAgB2B,YAAY,CAACD;QAC5C,EAAE,OAAOhC,OAAO;YACd,MAAM2B,UAAU3B,iBAAiB4B,QAAQ5B,MAAM2B,OAAO,GAAG;YACzD,MAAM,IAAIC,MAAM,CAAC,yBAAyB,EAAED,SAAS;QACvD;IACF;IAGA,MAAMO,YAAY,IAAI1D;IACtB,MAAM+B,OAAO4B,OAAO,CAACD;IAGrBnC,QAAQC,KAAK,CAAC;AAChB;AAGAE,OAAOkC,KAAK,CAAC,CAACpC;IACZD,QAAQC,KAAK,CAAC,gBAAgBA;IAC9BX,QAAQY,IAAI,CAAC;AACf"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { RepositoriesAPI } from '../api/repositories.js';
|
|
2
|
+
import { PullRequestsAPI } from '../api/pullrequests.js';
|
|
3
|
+
export const resourceTemplates = [
|
|
4
|
+
{
|
|
5
|
+
uriTemplate: 'bitbucket://repository/{workspace}/{repo_slug}',
|
|
6
|
+
name: 'Repository',
|
|
7
|
+
description: 'A Bitbucket repository with its metadata, branches, and settings',
|
|
8
|
+
mimeType: 'application/json'
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
uriTemplate: 'bitbucket://pullrequest/{workspace}/{repo_slug}/{pr_id}',
|
|
12
|
+
name: 'Pull Request',
|
|
13
|
+
description: 'A Bitbucket pull request with its details, comments, and diff',
|
|
14
|
+
mimeType: 'application/json'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
uriTemplate: 'bitbucket://file/{workspace}/{repo_slug}/{path}',
|
|
18
|
+
name: 'File',
|
|
19
|
+
description: 'A file from a Bitbucket repository',
|
|
20
|
+
mimeType: 'text/plain'
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
export class ResourceHandler {
|
|
24
|
+
repos;
|
|
25
|
+
prs;
|
|
26
|
+
constructor(client){
|
|
27
|
+
this.repos = new RepositoriesAPI(client);
|
|
28
|
+
this.prs = new PullRequestsAPI(client);
|
|
29
|
+
}
|
|
30
|
+
parseUri(uri) {
|
|
31
|
+
const repoMatch = uri.match(/^bitbucket:\/\/repository\/([^/]+)\/([^/]+)$/);
|
|
32
|
+
if (repoMatch) {
|
|
33
|
+
return {
|
|
34
|
+
type: 'repository',
|
|
35
|
+
params: {
|
|
36
|
+
workspace: repoMatch[1],
|
|
37
|
+
repo_slug: repoMatch[2]
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const prMatch = uri.match(/^bitbucket:\/\/pullrequest\/([^/]+)\/([^/]+)\/(\d+)$/);
|
|
42
|
+
if (prMatch) {
|
|
43
|
+
return {
|
|
44
|
+
type: 'pullrequest',
|
|
45
|
+
params: {
|
|
46
|
+
workspace: prMatch[1],
|
|
47
|
+
repo_slug: prMatch[2],
|
|
48
|
+
pr_id: prMatch[3]
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const fileMatch = uri.match(/^bitbucket:\/\/file\/([^/]+)\/([^/]+)\/(.+)$/);
|
|
53
|
+
if (fileMatch) {
|
|
54
|
+
return {
|
|
55
|
+
type: 'file',
|
|
56
|
+
params: {
|
|
57
|
+
workspace: fileMatch[1],
|
|
58
|
+
repo_slug: fileMatch[2],
|
|
59
|
+
path: fileMatch[3]
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
async readResource(uri) {
|
|
66
|
+
const parsed = this.parseUri(uri);
|
|
67
|
+
if (!parsed) {
|
|
68
|
+
throw new Error(`Invalid resource URI: ${uri}`);
|
|
69
|
+
}
|
|
70
|
+
switch(parsed.type){
|
|
71
|
+
case 'repository':
|
|
72
|
+
{
|
|
73
|
+
const repo = await this.repos.get({
|
|
74
|
+
workspace: parsed.params.workspace,
|
|
75
|
+
repo_slug: parsed.params.repo_slug
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
contents: [
|
|
79
|
+
{
|
|
80
|
+
uri,
|
|
81
|
+
mimeType: 'application/json',
|
|
82
|
+
text: JSON.stringify(repo, null, 2)
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
case 'pullrequest':
|
|
88
|
+
{
|
|
89
|
+
const pr = await this.prs.get(parsed.params.workspace, parsed.params.repo_slug, parseInt(parsed.params.pr_id, 10));
|
|
90
|
+
return {
|
|
91
|
+
contents: [
|
|
92
|
+
{
|
|
93
|
+
uri,
|
|
94
|
+
mimeType: 'application/json',
|
|
95
|
+
text: JSON.stringify(pr, null, 2)
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case 'file':
|
|
101
|
+
{
|
|
102
|
+
const content = await this.repos.getFileContent(parsed.params.workspace, parsed.params.repo_slug, parsed.params.path);
|
|
103
|
+
return {
|
|
104
|
+
contents: [
|
|
105
|
+
{
|
|
106
|
+
uri,
|
|
107
|
+
mimeType: 'text/plain',
|
|
108
|
+
text: content
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
default:
|
|
114
|
+
throw new Error(`Unknown resource type: ${parsed.type}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
listResourceTemplates() {
|
|
118
|
+
return resourceTemplates;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/resources/index.ts"],"sourcesContent":["import type { BitbucketClient } from '../api/client.js';\nimport { RepositoriesAPI } from '../api/repositories.js';\nimport { PullRequestsAPI } from '../api/pullrequests.js';\n\n// Resource URI templates\nexport const resourceTemplates = [\n {\n uriTemplate: 'bitbucket://repository/{workspace}/{repo_slug}',\n name: 'Repository',\n description: 'A Bitbucket repository with its metadata, branches, and settings',\n mimeType: 'application/json',\n },\n {\n uriTemplate: 'bitbucket://pullrequest/{workspace}/{repo_slug}/{pr_id}',\n name: 'Pull Request',\n description: 'A Bitbucket pull request with its details, comments, and diff',\n mimeType: 'application/json',\n },\n {\n uriTemplate: 'bitbucket://file/{workspace}/{repo_slug}/{path}',\n name: 'File',\n description: 'A file from a Bitbucket repository',\n mimeType: 'text/plain',\n },\n];\n\n// Resource handler class\nexport class ResourceHandler {\n private repos: RepositoriesAPI;\n private prs: PullRequestsAPI;\n\n constructor(client: BitbucketClient) {\n this.repos = new RepositoriesAPI(client);\n this.prs = new PullRequestsAPI(client);\n }\n\n /**\n * Parse a resource URI into its components\n */\n parseUri(uri: string): { type: string; params: Record<string, string> } | null {\n // Repository: bitbucket://repository/{workspace}/{repo_slug}\n const repoMatch = uri.match(/^bitbucket:\\/\\/repository\\/([^/]+)\\/([^/]+)$/);\n if (repoMatch) {\n return {\n type: 'repository',\n params: {\n workspace: repoMatch[1],\n repo_slug: repoMatch[2],\n },\n };\n }\n\n // Pull Request: bitbucket://pullrequest/{workspace}/{repo_slug}/{pr_id}\n const prMatch = uri.match(/^bitbucket:\\/\\/pullrequest\\/([^/]+)\\/([^/]+)\\/(\\d+)$/);\n if (prMatch) {\n return {\n type: 'pullrequest',\n params: {\n workspace: prMatch[1],\n repo_slug: prMatch[2],\n pr_id: prMatch[3],\n },\n };\n }\n\n // File: bitbucket://file/{workspace}/{repo_slug}/{path}\n const fileMatch = uri.match(/^bitbucket:\\/\\/file\\/([^/]+)\\/([^/]+)\\/(.+)$/);\n if (fileMatch) {\n return {\n type: 'file',\n params: {\n workspace: fileMatch[1],\n repo_slug: fileMatch[2],\n path: fileMatch[3],\n },\n };\n }\n\n return null;\n }\n\n /**\n * Read a resource by URI\n */\n async readResource(\n uri: string\n ): Promise<{ contents: Array<{ uri: string; mimeType: string; text: string }> }> {\n const parsed = this.parseUri(uri);\n if (!parsed) {\n throw new Error(`Invalid resource URI: ${uri}`);\n }\n\n switch (parsed.type) {\n case 'repository': {\n const repo = await this.repos.get({\n workspace: parsed.params.workspace,\n repo_slug: parsed.params.repo_slug,\n });\n return {\n contents: [\n {\n uri,\n mimeType: 'application/json',\n text: JSON.stringify(repo, null, 2),\n },\n ],\n };\n }\n\n case 'pullrequest': {\n const pr = await this.prs.get(\n parsed.params.workspace,\n parsed.params.repo_slug,\n parseInt(parsed.params.pr_id, 10)\n );\n return {\n contents: [\n {\n uri,\n mimeType: 'application/json',\n text: JSON.stringify(pr, null, 2),\n },\n ],\n };\n }\n\n case 'file': {\n const content = await this.repos.getFileContent(\n parsed.params.workspace,\n parsed.params.repo_slug,\n parsed.params.path\n );\n return {\n contents: [\n {\n uri,\n mimeType: 'text/plain',\n text: content,\n },\n ],\n };\n }\n\n default:\n throw new Error(`Unknown resource type: ${parsed.type}`);\n }\n }\n\n /**\n * List available resources (returns templates since actual resources are dynamic)\n */\n listResourceTemplates() {\n return resourceTemplates;\n }\n}\n"],"names":["RepositoriesAPI","PullRequestsAPI","resourceTemplates","uriTemplate","name","description","mimeType","ResourceHandler","repos","prs","client","parseUri","uri","repoMatch","match","type","params","workspace","repo_slug","prMatch","pr_id","fileMatch","path","readResource","parsed","Error","repo","get","contents","text","JSON","stringify","pr","parseInt","content","getFileContent","listResourceTemplates"],"mappings":"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"}
|