opencode-gitlab-plugin 2.8.1 → 3.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/CHANGELOG.md +29 -0
- package/README.md +54 -12
- package/dist/index.d.ts +36 -4
- package/dist/index.js +174 -59
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +418 -50
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +2 -1
package/dist/mcp-server.cjs
CHANGED
|
@@ -28893,19 +28893,45 @@ var GitLabApiClient = class {
|
|
|
28893
28893
|
}
|
|
28894
28894
|
};
|
|
28895
28895
|
|
|
28896
|
-
// src/client/
|
|
28897
|
-
|
|
28896
|
+
// src/client/connection-pagination.ts
|
|
28897
|
+
var DEFAULT_PAGE_SIZE = 20;
|
|
28898
|
+
var MAX_PAGE_SIZE = 100;
|
|
28899
|
+
function normalizePageSize(value) {
|
|
28900
|
+
if (value === void 0 || value === 0) return void 0;
|
|
28901
|
+
if (!Number.isFinite(value)) return DEFAULT_PAGE_SIZE;
|
|
28902
|
+
return Math.min(Math.max(Math.floor(value), 1), MAX_PAGE_SIZE);
|
|
28903
|
+
}
|
|
28904
|
+
function buildConnectionVariables(options) {
|
|
28898
28905
|
const variables = {};
|
|
28899
|
-
|
|
28900
|
-
|
|
28901
|
-
|
|
28902
|
-
|
|
28906
|
+
const first = normalizePageSize(options?.first);
|
|
28907
|
+
const last = normalizePageSize(options?.last);
|
|
28908
|
+
if (first !== void 0 && last !== void 0) {
|
|
28909
|
+
throw new Error("Pagination accepts either 'first' or 'last', not both");
|
|
28910
|
+
}
|
|
28911
|
+
if (options?.after && last !== void 0) {
|
|
28912
|
+
throw new Error("Pagination cursor 'after' cannot be combined with 'last'");
|
|
28913
|
+
}
|
|
28914
|
+
if (options?.before && first !== void 0) {
|
|
28915
|
+
throw new Error("Pagination cursor 'before' cannot be combined with 'first'");
|
|
28916
|
+
}
|
|
28917
|
+
if (first !== void 0) {
|
|
28918
|
+
variables.first = first;
|
|
28919
|
+
} else if (last !== void 0) {
|
|
28920
|
+
variables.last = last;
|
|
28921
|
+
} else if (options?.before) {
|
|
28922
|
+
variables.last = DEFAULT_PAGE_SIZE;
|
|
28923
|
+
} else {
|
|
28924
|
+
variables.first = DEFAULT_PAGE_SIZE;
|
|
28903
28925
|
}
|
|
28904
28926
|
if (options?.after) variables.after = options.after;
|
|
28905
|
-
if (options?.last !== void 0) variables.last = options.last;
|
|
28906
28927
|
if (options?.before) variables.before = options.before;
|
|
28907
28928
|
return variables;
|
|
28908
28929
|
}
|
|
28930
|
+
|
|
28931
|
+
// src/client/notes-types.ts
|
|
28932
|
+
function buildPaginationVariables(options) {
|
|
28933
|
+
return buildConnectionVariables(options);
|
|
28934
|
+
}
|
|
28909
28935
|
var NOTES_FRAGMENT = `
|
|
28910
28936
|
fragment NoteFields on Note {
|
|
28911
28937
|
id
|
|
@@ -28944,16 +28970,7 @@ var NOTES_CONNECTION_FRAGMENT = `
|
|
|
28944
28970
|
|
|
28945
28971
|
// src/client/discussions-types.ts
|
|
28946
28972
|
function buildDiscussionsPaginationVariables(options) {
|
|
28947
|
-
|
|
28948
|
-
if (options?.first !== void 0) {
|
|
28949
|
-
variables.first = options.first;
|
|
28950
|
-
} else if (options?.last == null) {
|
|
28951
|
-
variables.first = 20;
|
|
28952
|
-
}
|
|
28953
|
-
if (options?.after) variables.after = options.after;
|
|
28954
|
-
if (options?.last !== void 0) variables.last = options.last;
|
|
28955
|
-
if (options?.before) variables.before = options.before;
|
|
28956
|
-
return variables;
|
|
28973
|
+
return buildConnectionVariables(options);
|
|
28957
28974
|
}
|
|
28958
28975
|
var DISCUSSION_NOTE_FRAGMENT = `
|
|
28959
28976
|
fragment DiscussionNoteFields on Note {
|
|
@@ -30213,10 +30230,7 @@ var RepositoryClient = class extends GitLabApiClient {
|
|
|
30213
30230
|
async getFile(projectId, filePath, ref) {
|
|
30214
30231
|
const encodedProject = this.encodeProjectId(projectId);
|
|
30215
30232
|
const encodedPath = encodeURIComponent(filePath);
|
|
30216
|
-
|
|
30217
|
-
if (ref) {
|
|
30218
|
-
url2 += `?ref=${encodeURIComponent(ref)}`;
|
|
30219
|
-
}
|
|
30233
|
+
const url2 = `/projects/${encodedProject}/repository/files/${encodedPath}?ref=${encodeURIComponent(ref)}`;
|
|
30220
30234
|
const file2 = await this.fetch("GET", url2);
|
|
30221
30235
|
if (file2.encoding === "base64") {
|
|
30222
30236
|
return Buffer.from(file2.content, "base64").toString("utf-8");
|
|
@@ -32312,13 +32326,13 @@ var repositoryTools = {
|
|
|
32312
32326
|
gitlab_get_file: tool({
|
|
32313
32327
|
description: `Get the contents of a file from a repository.
|
|
32314
32328
|
Supports fetching files from any branch, tag, or commit SHA.
|
|
32315
|
-
|
|
32329
|
+
An explicit ref is required by the GitLab repository files API.
|
|
32316
32330
|
Note: Invalid refs will result in a 404 error from the GitLab API.`,
|
|
32317
32331
|
args: {
|
|
32318
32332
|
project_id: z5.string().describe("The project ID or URL-encoded path"),
|
|
32319
32333
|
file_path: z5.string().describe("Path to the file in the repository"),
|
|
32320
|
-
ref: z5.string().
|
|
32321
|
-
|
|
32334
|
+
ref: z5.string().trim().min(1).describe(
|
|
32335
|
+
"Branch name, tag, or commit SHA to fetch the file from. Supports full or short commit SHAs."
|
|
32322
32336
|
)
|
|
32323
32337
|
},
|
|
32324
32338
|
execute: async (args, _ctx) => {
|
|
@@ -32495,6 +32509,8 @@ Scopes and their requirements:
|
|
|
32495
32509
|
- wiki_blobs: Search wiki content (supports ref filter)
|
|
32496
32510
|
- group_projects: Search projects within a group (requires group_id)
|
|
32497
32511
|
|
|
32512
|
+
For scopes other than milestones, state="all" is treated as omitted because it does not filter results.
|
|
32513
|
+
|
|
32498
32514
|
Examples:
|
|
32499
32515
|
- Issues: scope="issues", search="bug", project_id="my-group/my-project"
|
|
32500
32516
|
- Code: scope="blobs", search="function calculateTotal"
|
|
@@ -32529,6 +32545,9 @@ Examples:
|
|
|
32529
32545
|
state: z6.enum(["active", "closed", "all"]).optional().describe("Filter by state (for milestones scope)")
|
|
32530
32546
|
},
|
|
32531
32547
|
execute: async (args, _ctx) => {
|
|
32548
|
+
if (args.scope !== "milestones" && args.state === "all") {
|
|
32549
|
+
args.state = void 0;
|
|
32550
|
+
}
|
|
32532
32551
|
validateSearchParams(args.scope, {
|
|
32533
32552
|
project_id: args.project_id,
|
|
32534
32553
|
group_id: args.group_id,
|
|
@@ -33230,10 +33249,10 @@ Examples:
|
|
|
33230
33249
|
"Filter by resolved status: true for resolved, false for unresolved. Only returns resolvable discussions (excludes system notes). Client-side filtering."
|
|
33231
33250
|
),
|
|
33232
33251
|
// Pagination
|
|
33233
|
-
first: z13.number().optional().describe("Number of items to return (default: 20)"),
|
|
33252
|
+
first: z13.number().int().min(0).max(100).optional().describe("Number of items to return (default: 20, max: 100; 0 uses the default)"),
|
|
33234
33253
|
after: z13.string().optional().describe("Cursor for pagination - use endCursor from previous response"),
|
|
33235
33254
|
before: z13.string().optional().describe("Cursor for backward pagination"),
|
|
33236
|
-
last: z13.number().optional().describe("Number of items from the end")
|
|
33255
|
+
last: z13.number().int().min(0).max(100).optional().describe("Number of items from the end (max: 100; 0 is omitted)")
|
|
33237
33256
|
},
|
|
33238
33257
|
execute: async (args, _ctx) => {
|
|
33239
33258
|
validateResourceParams(args.resource_type, args);
|
|
@@ -33545,9 +33564,11 @@ Examples:
|
|
|
33545
33564
|
"Filter by resolved status: true for resolved, false for unresolved. Only returns resolvable notes (excludes system notes). Client-side filtering."
|
|
33546
33565
|
),
|
|
33547
33566
|
// Pagination
|
|
33548
|
-
first: z14.number().
|
|
33567
|
+
first: z14.number().int().min(0).max(100).optional().describe(
|
|
33568
|
+
"Number of items to return from the beginning (default: 20, max: 100; 0 uses the default)"
|
|
33569
|
+
),
|
|
33549
33570
|
after: z14.string().optional().describe("Cursor for forward pagination - use endCursor from previous response"),
|
|
33550
|
-
last: z14.number().optional().describe("Number of items to return from the end (
|
|
33571
|
+
last: z14.number().int().min(0).max(100).optional().describe("Number of items to return from the end (max: 100; 0 is omitted)"),
|
|
33551
33572
|
before: z14.string().optional().describe("Cursor for backward pagination - use startCursor from previous response")
|
|
33552
33573
|
},
|
|
33553
33574
|
execute: async (args, _ctx) => {
|
|
@@ -34178,6 +34199,372 @@ Examples:
|
|
|
34178
34199
|
})
|
|
34179
34200
|
};
|
|
34180
34201
|
|
|
34202
|
+
// src/tools/orbit.ts
|
|
34203
|
+
var z18 = tool.schema;
|
|
34204
|
+
var orbitTools = {
|
|
34205
|
+
gitlab_orbit_status: tool({
|
|
34206
|
+
description: `Check GitLab Orbit Knowledge Graph API availability and health.
|
|
34207
|
+
|
|
34208
|
+
Returns the status of the Orbit API for the connected GitLab instance.
|
|
34209
|
+
Orbit is GitLab's Knowledge Graph API that provides rich SDLC graph data.
|
|
34210
|
+
|
|
34211
|
+
Note: Orbit is only available on Premium/Ultimate tiers and requires the feature flag to be enabled.
|
|
34212
|
+
If unavailable, returns an error with suggestions for REST API alternatives.`,
|
|
34213
|
+
args: {},
|
|
34214
|
+
execute: async (_args, _ctx) => {
|
|
34215
|
+
const client = getGitLabClient();
|
|
34216
|
+
try {
|
|
34217
|
+
const status = await client.fetch("GET", "/orbit/status");
|
|
34218
|
+
return JSON.stringify(
|
|
34219
|
+
{
|
|
34220
|
+
...status,
|
|
34221
|
+
available: true,
|
|
34222
|
+
message: "Orbit Knowledge Graph API is available"
|
|
34223
|
+
},
|
|
34224
|
+
null,
|
|
34225
|
+
2
|
|
34226
|
+
);
|
|
34227
|
+
} catch (error45) {
|
|
34228
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
34229
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
34230
|
+
return JSON.stringify(
|
|
34231
|
+
{
|
|
34232
|
+
available: false,
|
|
34233
|
+
error: "Orbit API not available",
|
|
34234
|
+
reason: "The Orbit Knowledge Graph API is not enabled on this GitLab instance. Orbit requires GitLab Premium/Ultimate tier and the feature flag to be enabled.",
|
|
34235
|
+
alternatives: [
|
|
34236
|
+
"Use REST API endpoints for MRs, issues, pipelines",
|
|
34237
|
+
"Use GraphQL API for complex queries",
|
|
34238
|
+
"Use gitlab_search for finding related entities"
|
|
34239
|
+
]
|
|
34240
|
+
},
|
|
34241
|
+
null,
|
|
34242
|
+
2
|
|
34243
|
+
);
|
|
34244
|
+
}
|
|
34245
|
+
if (errorMessage.includes("403") || errorMessage.includes("forbidden")) {
|
|
34246
|
+
return JSON.stringify(
|
|
34247
|
+
{
|
|
34248
|
+
available: false,
|
|
34249
|
+
error: "Orbit API access forbidden",
|
|
34250
|
+
reason: "Your access token does not have permission to use the Orbit API. This may require specific scopes or Premium/Ultimate tier.",
|
|
34251
|
+
alternatives: [
|
|
34252
|
+
"Check token scopes include api or read_api",
|
|
34253
|
+
"Verify your GitLab tier supports Orbit",
|
|
34254
|
+
"Use REST/GraphQL APIs as fallback"
|
|
34255
|
+
]
|
|
34256
|
+
},
|
|
34257
|
+
null,
|
|
34258
|
+
2
|
|
34259
|
+
);
|
|
34260
|
+
}
|
|
34261
|
+
if (errorMessage.includes("502")) {
|
|
34262
|
+
return JSON.stringify(
|
|
34263
|
+
{
|
|
34264
|
+
available: false,
|
|
34265
|
+
error: "Orbit API temporarily unavailable",
|
|
34266
|
+
reason: "The Orbit API returned a 502 error. This may be temporary.",
|
|
34267
|
+
suggestion: "Try again later or use REST/GraphQL APIs as fallback."
|
|
34268
|
+
},
|
|
34269
|
+
null,
|
|
34270
|
+
2
|
|
34271
|
+
);
|
|
34272
|
+
}
|
|
34273
|
+
return JSON.stringify(
|
|
34274
|
+
{
|
|
34275
|
+
available: false,
|
|
34276
|
+
error: "Orbit API error",
|
|
34277
|
+
message: errorMessage,
|
|
34278
|
+
alternatives: ["Use REST/GraphQL APIs as fallback"]
|
|
34279
|
+
},
|
|
34280
|
+
null,
|
|
34281
|
+
2
|
|
34282
|
+
);
|
|
34283
|
+
}
|
|
34284
|
+
}
|
|
34285
|
+
}),
|
|
34286
|
+
gitlab_orbit_schema: tool({
|
|
34287
|
+
description: `Get the Orbit Knowledge Graph schema.
|
|
34288
|
+
|
|
34289
|
+
Returns the available node types, relationship types, and their properties.
|
|
34290
|
+
Use this to understand what entities and relationships can be queried.
|
|
34291
|
+
|
|
34292
|
+
Node type domains include:
|
|
34293
|
+
- Core: Project, Group, User
|
|
34294
|
+
- Source Code: File, Definition, Commit
|
|
34295
|
+
- Code Review: MergeRequest, Diff, Review
|
|
34296
|
+
- CI/CD: Pipeline, Job, Deployment
|
|
34297
|
+
- Planning: WorkItem (issues, epics, tasks)
|
|
34298
|
+
- Security: Vulnerability
|
|
34299
|
+
|
|
34300
|
+
Note: Requires Orbit API to be available (Premium/Ultimate tier).`,
|
|
34301
|
+
args: {},
|
|
34302
|
+
execute: async (_args, _ctx) => {
|
|
34303
|
+
const client = getGitLabClient();
|
|
34304
|
+
try {
|
|
34305
|
+
const schema = await client.fetch("GET", "/orbit/schema");
|
|
34306
|
+
return JSON.stringify(schema, null, 2);
|
|
34307
|
+
} catch (error45) {
|
|
34308
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
34309
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
34310
|
+
return JSON.stringify(
|
|
34311
|
+
{
|
|
34312
|
+
error: "Orbit API not available",
|
|
34313
|
+
suggestion: "Use gitlab_orbit_status to check API availability"
|
|
34314
|
+
},
|
|
34315
|
+
null,
|
|
34316
|
+
2
|
|
34317
|
+
);
|
|
34318
|
+
}
|
|
34319
|
+
if (errorMessage.includes("502")) {
|
|
34320
|
+
return JSON.stringify(
|
|
34321
|
+
{
|
|
34322
|
+
error: "Orbit API temporarily unavailable",
|
|
34323
|
+
suggestion: "The Orbit API returned a 502 error. This may be temporary - try again later."
|
|
34324
|
+
},
|
|
34325
|
+
null,
|
|
34326
|
+
2
|
|
34327
|
+
);
|
|
34328
|
+
}
|
|
34329
|
+
throw error45;
|
|
34330
|
+
}
|
|
34331
|
+
}
|
|
34332
|
+
}),
|
|
34333
|
+
gitlab_orbit_query: tool({
|
|
34334
|
+
description: `Execute a query against the Orbit Knowledge Graph.
|
|
34335
|
+
|
|
34336
|
+
Query types:
|
|
34337
|
+
- traversal: Walk the graph from a starting node
|
|
34338
|
+
- neighbors: Get immediate neighbors of a node
|
|
34339
|
+
|
|
34340
|
+
IMPORTANT: The query format uses variable names for node references.
|
|
34341
|
+
- "id" in node definition is a VARIABLE NAME (alias), not the actual entity ID
|
|
34342
|
+
- Actual IDs go in "filters" with {"field": {"op": "eq", "value": X}} format
|
|
34343
|
+
|
|
34344
|
+
Example neighbors query to get a project's connections:
|
|
34345
|
+
{
|
|
34346
|
+
"query_type": "neighbors",
|
|
34347
|
+
"node": {
|
|
34348
|
+
"entity": "Project",
|
|
34349
|
+
"id": "p",
|
|
34350
|
+
"filters": {"id": {"op": "eq", "value": 278964}}
|
|
34351
|
+
},
|
|
34352
|
+
"neighbors": {"node": "p", "direction": "incoming"},
|
|
34353
|
+
"aggregations": [],
|
|
34354
|
+
"path": {"type": "shortest", "from": "p", "to": "p", "max_depth": 1}
|
|
34355
|
+
}
|
|
34356
|
+
|
|
34357
|
+
Example traversal query:
|
|
34358
|
+
{
|
|
34359
|
+
"query_type": "traversal",
|
|
34360
|
+
"node": {
|
|
34361
|
+
"entity": "MergeRequest",
|
|
34362
|
+
"id": "mr",
|
|
34363
|
+
"filters": {"id": {"op": "eq", "value": 377844873}}
|
|
34364
|
+
},
|
|
34365
|
+
"neighbors": {"node": "mr", "direction": "both"},
|
|
34366
|
+
"aggregations": [],
|
|
34367
|
+
"path": {"type": "shortest", "from": "mr", "to": "mr", "max_depth": 1}
|
|
34368
|
+
}
|
|
34369
|
+
|
|
34370
|
+
Note: Use gitlab_get_merge_request to get the internal ID from an IID.
|
|
34371
|
+
Returns nodes array and edges array with relationship types.`,
|
|
34372
|
+
args: {
|
|
34373
|
+
// Using a JSON string rather than a structured zod schema because the Orbit query
|
|
34374
|
+
// format is still evolving. This allows flexibility as the API changes without
|
|
34375
|
+
// requiring schema updates for each iteration.
|
|
34376
|
+
query: z18.string().describe(
|
|
34377
|
+
"JSON string containing the full query object. Must include query_type, node, neighbors, aggregations, and path fields. See description for format details."
|
|
34378
|
+
),
|
|
34379
|
+
limit: z18.number().optional().describe("Maximum number of results (default: 30)")
|
|
34380
|
+
},
|
|
34381
|
+
execute: async (args, _ctx) => {
|
|
34382
|
+
const client = getGitLabClient();
|
|
34383
|
+
let queryObj;
|
|
34384
|
+
try {
|
|
34385
|
+
queryObj = JSON.parse(args.query);
|
|
34386
|
+
} catch {
|
|
34387
|
+
return JSON.stringify(
|
|
34388
|
+
{
|
|
34389
|
+
error: "Invalid JSON in query parameter",
|
|
34390
|
+
suggestion: "Ensure the query parameter is valid JSON"
|
|
34391
|
+
},
|
|
34392
|
+
null,
|
|
34393
|
+
2
|
|
34394
|
+
);
|
|
34395
|
+
}
|
|
34396
|
+
const requestBody = {
|
|
34397
|
+
query: queryObj
|
|
34398
|
+
};
|
|
34399
|
+
if (args.limit) {
|
|
34400
|
+
requestBody.limit = args.limit;
|
|
34401
|
+
}
|
|
34402
|
+
try {
|
|
34403
|
+
const result = await client.fetch("POST", "/orbit/query", requestBody);
|
|
34404
|
+
if (result.code) {
|
|
34405
|
+
return JSON.stringify(
|
|
34406
|
+
{
|
|
34407
|
+
error: "Query compilation error",
|
|
34408
|
+
code: result.code,
|
|
34409
|
+
message: result.message,
|
|
34410
|
+
suggestion: 'Check query structure. Node "id" is a variable name, actual IDs go in filters.'
|
|
34411
|
+
},
|
|
34412
|
+
null,
|
|
34413
|
+
2
|
|
34414
|
+
);
|
|
34415
|
+
}
|
|
34416
|
+
return JSON.stringify(result, null, 2);
|
|
34417
|
+
} catch (error45) {
|
|
34418
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
34419
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
34420
|
+
return JSON.stringify(
|
|
34421
|
+
{
|
|
34422
|
+
error: "Orbit API not available",
|
|
34423
|
+
suggestion: "Use gitlab_orbit_status to check API availability"
|
|
34424
|
+
},
|
|
34425
|
+
null,
|
|
34426
|
+
2
|
|
34427
|
+
);
|
|
34428
|
+
}
|
|
34429
|
+
if (errorMessage.includes("502")) {
|
|
34430
|
+
return JSON.stringify(
|
|
34431
|
+
{
|
|
34432
|
+
error: "Orbit query failed",
|
|
34433
|
+
message: "The Orbit API returned a 502 error. This is a known issue with some entity types (e.g., source_code domain).",
|
|
34434
|
+
suggestion: "Try a different entity type or use REST/GraphQL APIs as fallback."
|
|
34435
|
+
},
|
|
34436
|
+
null,
|
|
34437
|
+
2
|
|
34438
|
+
);
|
|
34439
|
+
}
|
|
34440
|
+
throw error45;
|
|
34441
|
+
}
|
|
34442
|
+
}
|
|
34443
|
+
}),
|
|
34444
|
+
gitlab_orbit_neighbors: tool({
|
|
34445
|
+
description: `Get immediate neighbors of a node in the Orbit Knowledge Graph.
|
|
34446
|
+
|
|
34447
|
+
This is a simplified wrapper that builds the query for you.
|
|
34448
|
+
Returns nodes directly connected to the specified node.
|
|
34449
|
+
|
|
34450
|
+
Common use cases:
|
|
34451
|
+
- Get all connections to a project (users, groups, pipelines, MRs)
|
|
34452
|
+
- Get pipelines for an MR
|
|
34453
|
+
- Get files changed in a commit
|
|
34454
|
+
- Get deployments from a pipeline
|
|
34455
|
+
|
|
34456
|
+
Note: Use GitLab internal numeric IDs (not IIDs).
|
|
34457
|
+
Example: For MR !123 in gitlab-org/gitlab, first get the internal ID via gitlab_get_merge_request,
|
|
34458
|
+
then use that ID (e.g., 377844873).`,
|
|
34459
|
+
args: {
|
|
34460
|
+
entity_type: z18.string().describe(
|
|
34461
|
+
"Type of the entity (e.g., Project, MergeRequest, User, Pipeline, Commit, WorkItem, Group)"
|
|
34462
|
+
),
|
|
34463
|
+
entity_id: z18.number().describe(
|
|
34464
|
+
"GitLab internal numeric ID (not IID). Get this from REST/GraphQL API responses."
|
|
34465
|
+
),
|
|
34466
|
+
direction: z18.enum(["incoming", "outgoing", "both"]).optional().describe("Direction of relationships to traverse (default: both)"),
|
|
34467
|
+
limit: z18.number().optional().describe("Maximum number of neighbors to return (default: 30)")
|
|
34468
|
+
},
|
|
34469
|
+
execute: async (args, _ctx) => {
|
|
34470
|
+
const client = getGitLabClient();
|
|
34471
|
+
const direction = args.direction || "both";
|
|
34472
|
+
const queryObj = {
|
|
34473
|
+
query_type: "neighbors",
|
|
34474
|
+
node: {
|
|
34475
|
+
entity: args.entity_type,
|
|
34476
|
+
id: "n",
|
|
34477
|
+
filters: {
|
|
34478
|
+
id: { op: "eq", value: args.entity_id }
|
|
34479
|
+
}
|
|
34480
|
+
},
|
|
34481
|
+
neighbors: {
|
|
34482
|
+
node: "n",
|
|
34483
|
+
direction
|
|
34484
|
+
},
|
|
34485
|
+
aggregations: [],
|
|
34486
|
+
path: { type: "shortest", from: "n", to: "n", max_depth: 1 }
|
|
34487
|
+
};
|
|
34488
|
+
const requestBody = {
|
|
34489
|
+
query: queryObj
|
|
34490
|
+
};
|
|
34491
|
+
if (args.limit) {
|
|
34492
|
+
requestBody.limit = args.limit;
|
|
34493
|
+
}
|
|
34494
|
+
try {
|
|
34495
|
+
const result = await client.fetch("POST", "/orbit/query", requestBody);
|
|
34496
|
+
if (result.code) {
|
|
34497
|
+
return JSON.stringify(
|
|
34498
|
+
{
|
|
34499
|
+
error: "Query error",
|
|
34500
|
+
code: result.code,
|
|
34501
|
+
message: result.message,
|
|
34502
|
+
entity_type: args.entity_type,
|
|
34503
|
+
entity_id: args.entity_id
|
|
34504
|
+
},
|
|
34505
|
+
null,
|
|
34506
|
+
2
|
|
34507
|
+
);
|
|
34508
|
+
}
|
|
34509
|
+
return JSON.stringify(result, null, 2);
|
|
34510
|
+
} catch (error45) {
|
|
34511
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
34512
|
+
if (errorMessage.includes("404") || errorMessage.includes("not found")) {
|
|
34513
|
+
return JSON.stringify(
|
|
34514
|
+
{
|
|
34515
|
+
error: "Orbit API not available",
|
|
34516
|
+
entity_type: args.entity_type,
|
|
34517
|
+
entity_id: args.entity_id,
|
|
34518
|
+
suggestion: "Use gitlab_orbit_status to check API availability"
|
|
34519
|
+
},
|
|
34520
|
+
null,
|
|
34521
|
+
2
|
|
34522
|
+
);
|
|
34523
|
+
}
|
|
34524
|
+
if (errorMessage.includes("502")) {
|
|
34525
|
+
return JSON.stringify(
|
|
34526
|
+
{
|
|
34527
|
+
error: "Orbit query failed",
|
|
34528
|
+
entity_type: args.entity_type,
|
|
34529
|
+
entity_id: args.entity_id,
|
|
34530
|
+
message: "The Orbit API returned a 502 error. This is a known issue with some entity types.",
|
|
34531
|
+
suggestion: "Try a different entity type or use REST/GraphQL APIs as fallback."
|
|
34532
|
+
},
|
|
34533
|
+
null,
|
|
34534
|
+
2
|
|
34535
|
+
);
|
|
34536
|
+
}
|
|
34537
|
+
throw error45;
|
|
34538
|
+
}
|
|
34539
|
+
}
|
|
34540
|
+
})
|
|
34541
|
+
};
|
|
34542
|
+
|
|
34543
|
+
// src/tools/index.ts
|
|
34544
|
+
var mcpTools = {
|
|
34545
|
+
...mergeRequestTools,
|
|
34546
|
+
...issueTools,
|
|
34547
|
+
...epicTools,
|
|
34548
|
+
...pipelineTools,
|
|
34549
|
+
...repositoryTools,
|
|
34550
|
+
...searchTools,
|
|
34551
|
+
...projectTools,
|
|
34552
|
+
...userTools,
|
|
34553
|
+
...securityTools,
|
|
34554
|
+
...todoTools,
|
|
34555
|
+
...wikiTools,
|
|
34556
|
+
...workItemTools,
|
|
34557
|
+
...discussionsUnifiedTools,
|
|
34558
|
+
...notesUnifiedTools,
|
|
34559
|
+
...gitTools,
|
|
34560
|
+
...auditTools,
|
|
34561
|
+
...awardEmojiTools
|
|
34562
|
+
};
|
|
34563
|
+
var allTools = {
|
|
34564
|
+
...mcpTools,
|
|
34565
|
+
...orbitTools
|
|
34566
|
+
};
|
|
34567
|
+
|
|
34181
34568
|
// src/mcp-adapter.ts
|
|
34182
34569
|
function adaptToolsToMcp(server, tools) {
|
|
34183
34570
|
for (const [name, def] of Object.entries(tools)) {
|
|
@@ -34203,31 +34590,12 @@ async function main() {
|
|
|
34203
34590
|
`);
|
|
34204
34591
|
process.exit(1);
|
|
34205
34592
|
}
|
|
34206
|
-
const
|
|
34207
|
-
...mergeRequestTools,
|
|
34208
|
-
...issueTools,
|
|
34209
|
-
...epicTools,
|
|
34210
|
-
...pipelineTools,
|
|
34211
|
-
...repositoryTools,
|
|
34212
|
-
...searchTools,
|
|
34213
|
-
...projectTools,
|
|
34214
|
-
...userTools,
|
|
34215
|
-
...securityTools,
|
|
34216
|
-
...todoTools,
|
|
34217
|
-
...wikiTools,
|
|
34218
|
-
...workItemTools,
|
|
34219
|
-
...discussionsUnifiedTools,
|
|
34220
|
-
...notesUnifiedTools,
|
|
34221
|
-
...gitTools,
|
|
34222
|
-
...auditTools,
|
|
34223
|
-
...awardEmojiTools
|
|
34224
|
-
};
|
|
34225
|
-
const version2 = true ? "2.8.1" : "0.0.0";
|
|
34593
|
+
const version2 = true ? "3.0.0" : "0.0.0";
|
|
34226
34594
|
const server = new McpServer({ name: "gitlab", version: version2 });
|
|
34227
|
-
adaptToolsToMcp(server,
|
|
34595
|
+
adaptToolsToMcp(server, mcpTools);
|
|
34228
34596
|
const transport = new StdioServerTransport();
|
|
34229
34597
|
await server.connect(transport);
|
|
34230
|
-
const toolCount = Object.keys(
|
|
34598
|
+
const toolCount = Object.keys(mcpTools).length;
|
|
34231
34599
|
process.stderr.write(`gitlab MCP: serving ${toolCount} tools via stdio
|
|
34232
34600
|
`);
|
|
34233
34601
|
process.on("SIGINT", async () => {
|