mcp-server-bitbucket 0.11.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/Dockerfile +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2976 -0
- package/docker-entrypoint.sh +6 -0
- package/package.json +57 -0
- package/smithery.yaml +47 -0
- package/src/client.ts +1102 -0
- package/src/index.ts +149 -0
- package/src/prompts.ts +208 -0
- package/src/resources.ts +160 -0
- package/src/settings.ts +63 -0
- package/src/tools/branches.ts +69 -0
- package/src/tools/commits.ts +186 -0
- package/src/tools/deployments.ts +100 -0
- package/src/tools/index.ts +112 -0
- package/src/tools/permissions.ts +205 -0
- package/src/tools/pipelines.ts +301 -0
- package/src/tools/projects.ts +63 -0
- package/src/tools/pull-requests.ts +321 -0
- package/src/tools/repositories.ts +208 -0
- package/src/tools/restrictions.ts +94 -0
- package/src/tools/source.ts +78 -0
- package/src/tools/tags.ts +88 -0
- package/src/tools/webhooks.ts +121 -0
- package/src/types.ts +369 -0
- package/src/utils.ts +83 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository tools for Bitbucket MCP Server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { getClient } from '../client.js';
|
|
7
|
+
import { validateLimit, notFoundResponse, sanitizeSearchTerm } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
export const definitions: Tool[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'get_repository',
|
|
12
|
+
description: 'Get information about a Bitbucket repository.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
repo_slug: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Repository slug (e.g., "anzsic_classifier")',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
required: ['repo_slug'],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'create_repository',
|
|
26
|
+
description: 'Create a new Bitbucket repository.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
repo_slug: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Repository slug (lowercase, no spaces)',
|
|
33
|
+
},
|
|
34
|
+
project_key: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Project key to create repo under (optional)',
|
|
37
|
+
},
|
|
38
|
+
is_private: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
description: 'Whether repository is private (default: true)',
|
|
41
|
+
default: true,
|
|
42
|
+
},
|
|
43
|
+
description: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Repository description',
|
|
46
|
+
default: '',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: ['repo_slug'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'delete_repository',
|
|
54
|
+
description: 'Delete a Bitbucket repository. WARNING: This action is irreversible!',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
repo_slug: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Repository slug to delete',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ['repo_slug'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'list_repositories',
|
|
68
|
+
description: 'List and search repositories in the workspace.',
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
project_key: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: 'Filter by project key (optional)',
|
|
75
|
+
},
|
|
76
|
+
search: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'Simple search term for repository name (optional). Uses fuzzy matching.',
|
|
79
|
+
},
|
|
80
|
+
query: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'Advanced Bitbucket query syntax (optional). Examples: name ~ "api", is_private = false',
|
|
83
|
+
},
|
|
84
|
+
limit: {
|
|
85
|
+
type: 'number',
|
|
86
|
+
description: 'Maximum number of results (default: 50, max: 100)',
|
|
87
|
+
default: 50,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
required: [],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'update_repository',
|
|
95
|
+
description: 'Update repository settings (project, visibility, description, name).',
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
repo_slug: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
description: 'Repository slug',
|
|
102
|
+
},
|
|
103
|
+
project_key: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
description: 'Move to different project (optional)',
|
|
106
|
+
},
|
|
107
|
+
is_private: {
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
description: 'Change visibility (optional)',
|
|
110
|
+
},
|
|
111
|
+
description: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
description: 'Update description (optional)',
|
|
114
|
+
},
|
|
115
|
+
name: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
description: 'Rename repository (optional)',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
required: ['repo_slug'],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
export const handlers: Record<string, (args: Record<string, unknown>) => Promise<Record<string, unknown>>> = {
|
|
126
|
+
get_repository: async (args) => {
|
|
127
|
+
const client = getClient();
|
|
128
|
+
const result = await client.getRepository(args.repo_slug as string);
|
|
129
|
+
if (!result) {
|
|
130
|
+
return notFoundResponse('Repository', args.repo_slug as string);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
name: result.name,
|
|
134
|
+
full_name: result.full_name,
|
|
135
|
+
private: result.is_private,
|
|
136
|
+
project: result.project?.key,
|
|
137
|
+
description: result.description || '',
|
|
138
|
+
main_branch: result.mainbranch?.name,
|
|
139
|
+
clone_urls: client.extractCloneUrls(result),
|
|
140
|
+
created: result.created_on,
|
|
141
|
+
updated: result.updated_on,
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
create_repository: async (args) => {
|
|
146
|
+
const client = getClient();
|
|
147
|
+
const result = await client.createRepository(args.repo_slug as string, {
|
|
148
|
+
projectKey: args.project_key as string | undefined,
|
|
149
|
+
isPrivate: args.is_private as boolean | undefined,
|
|
150
|
+
description: args.description as string | undefined,
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
name: result.name,
|
|
154
|
+
full_name: result.full_name,
|
|
155
|
+
clone_urls: client.extractCloneUrls(result),
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
delete_repository: async (args) => {
|
|
160
|
+
const client = getClient();
|
|
161
|
+
await client.deleteRepository(args.repo_slug as string);
|
|
162
|
+
return {};
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
list_repositories: async (args) => {
|
|
166
|
+
const client = getClient();
|
|
167
|
+
let effectiveQuery = args.query as string | undefined;
|
|
168
|
+
|
|
169
|
+
if (args.search && !args.query) {
|
|
170
|
+
const safeSearch = sanitizeSearchTerm(args.search as string);
|
|
171
|
+
effectiveQuery = `name ~ "${safeSearch}"`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const repos = await client.listRepositories({
|
|
175
|
+
projectKey: args.project_key as string | undefined,
|
|
176
|
+
query: effectiveQuery,
|
|
177
|
+
limit: validateLimit((args.limit as number) || 50),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
repositories: repos.map(r => ({
|
|
182
|
+
name: r.name,
|
|
183
|
+
full_name: r.full_name,
|
|
184
|
+
private: r.is_private,
|
|
185
|
+
project: r.project?.key,
|
|
186
|
+
description: r.description || '',
|
|
187
|
+
})),
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
update_repository: async (args) => {
|
|
192
|
+
const client = getClient();
|
|
193
|
+
const result = await client.updateRepository(args.repo_slug as string, {
|
|
194
|
+
projectKey: args.project_key as string | undefined,
|
|
195
|
+
isPrivate: args.is_private as boolean | undefined,
|
|
196
|
+
description: args.description as string | undefined,
|
|
197
|
+
name: args.name as string | undefined,
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
name: result.name,
|
|
201
|
+
full_name: result.full_name,
|
|
202
|
+
project: result.project?.key,
|
|
203
|
+
private: result.is_private,
|
|
204
|
+
description: result.description || '',
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch Restriction tools for Bitbucket MCP Server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { getClient } from '../client.js';
|
|
7
|
+
import { validateLimit } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
export const definitions: Tool[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'list_branch_restrictions',
|
|
12
|
+
description: 'List branch restrictions (protection rules) in a repository.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
17
|
+
limit: { type: 'number', description: 'Maximum results (default: 50)', default: 50 },
|
|
18
|
+
},
|
|
19
|
+
required: ['repo_slug'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'create_branch_restriction',
|
|
24
|
+
description: 'Create a branch restriction (protection rule).',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
29
|
+
kind: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'Type of restriction: push, force, delete, restrict_merges, require_passing_builds_to_merge, require_approvals_to_merge, etc.',
|
|
32
|
+
},
|
|
33
|
+
pattern: { type: 'string', description: 'Branch pattern (e.g., "main", "release/*"). Required for glob match.', default: '' },
|
|
34
|
+
branch_match_kind: { type: 'string', description: 'How to match branches: "glob" or "branching_model"', default: 'glob' },
|
|
35
|
+
branch_type: { type: 'string', description: 'Branch type when using branching_model: development, production, feature, etc.', default: '' },
|
|
36
|
+
value: { type: 'number', description: 'Numeric value (e.g., number of required approvals)', default: 0 },
|
|
37
|
+
},
|
|
38
|
+
required: ['repo_slug', 'kind'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'delete_branch_restriction',
|
|
43
|
+
description: 'Delete a branch restriction.',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
48
|
+
restriction_id: { type: 'number', description: 'Restriction ID' },
|
|
49
|
+
},
|
|
50
|
+
required: ['repo_slug', 'restriction_id'],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
export const handlers: Record<string, (args: Record<string, unknown>) => Promise<Record<string, unknown>>> = {
|
|
56
|
+
list_branch_restrictions: async (args) => {
|
|
57
|
+
const client = getClient();
|
|
58
|
+
const restrictions = await client.listBranchRestrictions(args.repo_slug as string, {
|
|
59
|
+
limit: validateLimit((args.limit as number) || 50),
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
restrictions: restrictions.map(r => ({
|
|
63
|
+
id: r.id,
|
|
64
|
+
kind: r.kind,
|
|
65
|
+
pattern: r.pattern,
|
|
66
|
+
branch_match_kind: r.branch_match_kind,
|
|
67
|
+
branch_type: r.branch_type,
|
|
68
|
+
value: r.value,
|
|
69
|
+
})),
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
create_branch_restriction: async (args) => {
|
|
74
|
+
const client = getClient();
|
|
75
|
+
const result = await client.createBranchRestriction(args.repo_slug as string, {
|
|
76
|
+
kind: args.kind as string,
|
|
77
|
+
pattern: args.pattern as string,
|
|
78
|
+
branchMatchKind: args.branch_match_kind as string || 'glob',
|
|
79
|
+
branchType: args.branch_type as string || undefined,
|
|
80
|
+
value: args.value as number || undefined,
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
id: result.id,
|
|
84
|
+
kind: result.kind,
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
delete_branch_restriction: async (args) => {
|
|
89
|
+
const client = getClient();
|
|
90
|
+
await client.deleteBranchRestriction(args.repo_slug as string, args.restriction_id as number);
|
|
91
|
+
return {};
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source (File Browsing) tools for Bitbucket MCP Server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { getClient } from '../client.js';
|
|
7
|
+
import { validateLimit } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
export const definitions: Tool[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'get_file_content',
|
|
12
|
+
description: 'Get the content of a file from a repository. Read file contents without cloning.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
17
|
+
path: { type: 'string', description: 'File path (e.g., "src/main.py", "README.md")' },
|
|
18
|
+
ref: { type: 'string', description: 'Branch, tag, or commit hash (default: "main")', default: 'main' },
|
|
19
|
+
},
|
|
20
|
+
required: ['repo_slug', 'path'],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'list_directory',
|
|
25
|
+
description: 'List contents of a directory in a repository. Browse repository structure without cloning.',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
30
|
+
path: { type: 'string', description: 'Directory path (empty string for root)', default: '' },
|
|
31
|
+
ref: { type: 'string', description: 'Branch, tag, or commit hash (default: "main")', default: 'main' },
|
|
32
|
+
limit: { type: 'number', description: 'Maximum entries (default: 100)', default: 100 },
|
|
33
|
+
},
|
|
34
|
+
required: ['repo_slug'],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export const handlers: Record<string, (args: Record<string, unknown>) => Promise<Record<string, unknown>>> = {
|
|
40
|
+
get_file_content: async (args) => {
|
|
41
|
+
const client = getClient();
|
|
42
|
+
const ref = (args.ref as string) || 'main';
|
|
43
|
+
const content = await client.getFileContent(args.repo_slug as string, args.path as string, ref);
|
|
44
|
+
|
|
45
|
+
if (content === null) {
|
|
46
|
+
return { error: `File '${args.path}' not found at ref '${ref}'` };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
path: args.path,
|
|
51
|
+
ref,
|
|
52
|
+
content,
|
|
53
|
+
size: content.length,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
list_directory: async (args) => {
|
|
58
|
+
const client = getClient();
|
|
59
|
+
const ref = (args.ref as string) || 'main';
|
|
60
|
+
const path = (args.path as string) || '';
|
|
61
|
+
|
|
62
|
+
const entries = await client.listDirectory(args.repo_slug as string, path, {
|
|
63
|
+
ref,
|
|
64
|
+
limit: validateLimit((args.limit as number) || 100),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
path: path || '/',
|
|
69
|
+
ref,
|
|
70
|
+
entries: entries.map(e => ({
|
|
71
|
+
path: e.path,
|
|
72
|
+
type: e.type === 'commit_directory' ? 'directory' : 'file',
|
|
73
|
+
size: e.size,
|
|
74
|
+
})),
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag tools for Bitbucket MCP Server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { getClient } from '../client.js';
|
|
7
|
+
import { validateLimit, truncateHash } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
export const definitions: Tool[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'list_tags',
|
|
12
|
+
description: 'List tags in a repository.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
17
|
+
limit: { type: 'number', description: 'Maximum results (default: 50)', default: 50 },
|
|
18
|
+
},
|
|
19
|
+
required: ['repo_slug'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'create_tag',
|
|
24
|
+
description: 'Create a new tag in a repository.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
29
|
+
name: { type: 'string', description: 'Tag name (e.g., "v1.0.0")' },
|
|
30
|
+
target: { type: 'string', description: 'Commit hash or branch name to tag' },
|
|
31
|
+
message: { type: 'string', description: 'Optional tag message (for annotated tags)', default: '' },
|
|
32
|
+
},
|
|
33
|
+
required: ['repo_slug', 'name', 'target'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'delete_tag',
|
|
38
|
+
description: 'Delete a tag from a repository.',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
43
|
+
tag_name: { type: 'string', description: 'Tag name to delete' },
|
|
44
|
+
},
|
|
45
|
+
required: ['repo_slug', 'tag_name'],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
export const handlers: Record<string, (args: Record<string, unknown>) => Promise<Record<string, unknown>>> = {
|
|
51
|
+
list_tags: async (args) => {
|
|
52
|
+
const client = getClient();
|
|
53
|
+
const tags = await client.listTags(args.repo_slug as string, {
|
|
54
|
+
limit: validateLimit((args.limit as number) || 50),
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
tags: tags.map(t => ({
|
|
58
|
+
name: t.name,
|
|
59
|
+
target: truncateHash(t.target?.hash),
|
|
60
|
+
message: t.message,
|
|
61
|
+
date: t.target?.date,
|
|
62
|
+
tagger: t.tagger?.raw,
|
|
63
|
+
})),
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
create_tag: async (args) => {
|
|
68
|
+
const client = getClient();
|
|
69
|
+
const result = await client.createTag(
|
|
70
|
+
args.repo_slug as string,
|
|
71
|
+
args.name as string,
|
|
72
|
+
args.target as string,
|
|
73
|
+
args.message as string || undefined
|
|
74
|
+
);
|
|
75
|
+
return {
|
|
76
|
+
name: result.name,
|
|
77
|
+
target: truncateHash(result.target?.hash),
|
|
78
|
+
message: result.message || '',
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
delete_tag: async (args) => {
|
|
83
|
+
const client = getClient();
|
|
84
|
+
await client.deleteTag(args.repo_slug as string, args.tag_name as string);
|
|
85
|
+
return {};
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook tools for Bitbucket MCP Server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { getClient } from '../client.js';
|
|
7
|
+
import { validateLimit, notFoundResponse } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
export const definitions: Tool[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'list_webhooks',
|
|
12
|
+
description: 'List webhooks configured for a repository.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
17
|
+
limit: { type: 'number', description: 'Maximum results (default: 50)', default: 50 },
|
|
18
|
+
},
|
|
19
|
+
required: ['repo_slug'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'create_webhook',
|
|
24
|
+
description: 'Create a webhook for a repository.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
29
|
+
url: { type: 'string', description: 'URL to call when events occur' },
|
|
30
|
+
events: {
|
|
31
|
+
type: 'array',
|
|
32
|
+
items: { type: 'string' },
|
|
33
|
+
description: 'List of events (e.g., repo:push, pullrequest:created, pullrequest:merged)',
|
|
34
|
+
},
|
|
35
|
+
description: { type: 'string', description: 'Webhook description (optional)', default: '' },
|
|
36
|
+
active: { type: 'boolean', description: 'Whether webhook is active (default: true)', default: true },
|
|
37
|
+
},
|
|
38
|
+
required: ['repo_slug', 'url', 'events'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'get_webhook',
|
|
43
|
+
description: 'Get details about a specific webhook.',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
48
|
+
webhook_uuid: { type: 'string', description: 'Webhook UUID' },
|
|
49
|
+
},
|
|
50
|
+
required: ['repo_slug', 'webhook_uuid'],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'delete_webhook',
|
|
55
|
+
description: 'Delete a webhook.',
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
repo_slug: { type: 'string', description: 'Repository slug' },
|
|
60
|
+
webhook_uuid: { type: 'string', description: 'Webhook UUID' },
|
|
61
|
+
},
|
|
62
|
+
required: ['repo_slug', 'webhook_uuid'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
export const handlers: Record<string, (args: Record<string, unknown>) => Promise<Record<string, unknown>>> = {
|
|
68
|
+
list_webhooks: async (args) => {
|
|
69
|
+
const client = getClient();
|
|
70
|
+
const webhooks = await client.listWebhooks(args.repo_slug as string, {
|
|
71
|
+
limit: validateLimit((args.limit as number) || 50),
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
webhooks: webhooks.map(w => ({
|
|
75
|
+
uuid: w.uuid,
|
|
76
|
+
url: w.url,
|
|
77
|
+
description: w.description,
|
|
78
|
+
events: w.events,
|
|
79
|
+
active: w.active,
|
|
80
|
+
})),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
create_webhook: async (args) => {
|
|
85
|
+
const client = getClient();
|
|
86
|
+
const result = await client.createWebhook(args.repo_slug as string, {
|
|
87
|
+
url: args.url as string,
|
|
88
|
+
events: args.events as string[],
|
|
89
|
+
description: args.description as string,
|
|
90
|
+
active: args.active as boolean ?? true,
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
uuid: result.uuid,
|
|
94
|
+
url: result.url,
|
|
95
|
+
events: result.events,
|
|
96
|
+
active: result.active,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
get_webhook: async (args) => {
|
|
101
|
+
const client = getClient();
|
|
102
|
+
const result = await client.getWebhook(args.repo_slug as string, args.webhook_uuid as string);
|
|
103
|
+
if (!result) {
|
|
104
|
+
return notFoundResponse('Webhook', args.webhook_uuid as string);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
uuid: result.uuid,
|
|
108
|
+
url: result.url,
|
|
109
|
+
description: result.description,
|
|
110
|
+
events: result.events,
|
|
111
|
+
active: result.active,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
delete_webhook: async (args) => {
|
|
116
|
+
const client = getClient();
|
|
117
|
+
await client.deleteWebhook(args.repo_slug as string, args.webhook_uuid as string);
|
|
118
|
+
return {};
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|