opencode-gitlab-plugin 3.0.0 → 3.0.1

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.
@@ -34201,6 +34201,50 @@ Examples:
34201
34201
 
34202
34202
  // src/tools/orbit.ts
34203
34203
  var z18 = tool.schema;
34204
+ function orbitRelTypes(entityType) {
34205
+ switch (entityType) {
34206
+ case "MergeRequest":
34207
+ return [
34208
+ "AUTHORED",
34209
+ "REVIEWER",
34210
+ "ASSIGNED",
34211
+ "CLOSES",
34212
+ "MENTIONS",
34213
+ "IN_PROJECT",
34214
+ "HAS_HEAD_PIPELINE",
34215
+ "TRIGGERED",
34216
+ "IN_MILESTONE",
34217
+ "HAS_LABEL"
34218
+ ];
34219
+ case "WorkItem":
34220
+ return [
34221
+ "AUTHORED",
34222
+ "ASSIGNED",
34223
+ "CLOSES",
34224
+ "MENTIONS",
34225
+ "RELATED_TO",
34226
+ "CONTAINS",
34227
+ "IN_PROJECT",
34228
+ "IN_GROUP",
34229
+ "IN_MILESTONE",
34230
+ "HAS_LABEL"
34231
+ ];
34232
+ case "User":
34233
+ return ["AUTHORED", "ASSIGNED", "REVIEWER", "MEMBER_OF", "OWNER", "CREATOR"];
34234
+ case "Project":
34235
+ return ["CONTAINS", "IN_PROJECT", "MEMBER_OF", "CREATOR"];
34236
+ case "Pipeline":
34237
+ return ["HAS_HEAD_PIPELINE", "TRIGGERED", "IN_PROJECT", "HAS_JOB", "HAS_STAGE"];
34238
+ case "File":
34239
+ return ["CONTAINS", "IMPORTS", "DEFINES", "CALLS", "ON_BRANCH"];
34240
+ case "Group":
34241
+ return ["CONTAINS", "MEMBER_OF", "OWNER", "IN_GROUP"];
34242
+ case "Commit":
34243
+ return ["MERGED_AT_COMMIT"];
34244
+ default:
34245
+ return ["AUTHORED", "ASSIGNED", "REVIEWER", "CLOSES", "MENTIONS", "RELATED_TO", "IN_PROJECT"];
34246
+ }
34247
+ }
34204
34248
  var orbitTools = {
34205
34249
  gitlab_orbit_status: tool({
34206
34250
  description: `Check GitLab Orbit Knowledge Graph API availability and health.
@@ -34334,47 +34378,64 @@ Note: Requires Orbit API to be available (Premium/Ultimate tier).`,
34334
34378
  description: `Execute a query against the Orbit Knowledge Graph.
34335
34379
 
34336
34380
  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:
34381
+ - traversal: Fetch matching nodes or follow relationships between nodes
34382
+ - neighbors: Return nodes connected to one bounded node
34383
+ - aggregation: Count/sum/avg/group graph results
34384
+ - path_finding: Find a bounded path between two nodes
34385
+
34386
+ QUERY SHAPE (current DSL, format_version 5.x):
34387
+ Every query has a "query_type" and a "nodes" array of node selectors (max 5).
34388
+ A node selector names an entity and is made SELECTIVE via "node_ids" (exact
34389
+ internal ids) or "filters" (natural properties). Relationships between selectors
34390
+ go in a "relationships" array referencing selector "id" aliases.
34391
+
34392
+ IMPORTANT changes from older docs:
34393
+ - Use a top-level "nodes" ARRAY, not a single "node" object.
34394
+ - Pin ids with "node_ids": [123], NOT filters:{id:{op:"eq",value:123}}.
34395
+ - Filters use {op: value}, e.g. {"state": "merged"} or {"created_at": {"gte": "2026-01-01"}}.
34396
+ - A "neighbors" query uses a 1-element "nodes" array plus a top-level
34397
+ "neighbors": {direction, rel_types:[...]} object. rel_types is REQUIRED to
34398
+ bound the lookup and is capped at 10 entries.
34399
+
34400
+ Example neighbors query (an MR's graph neighbours):
34345
34401
  {
34346
34402
  "query_type": "neighbors",
34347
- "node": {
34348
- "entity": "Project",
34349
- "id": "p",
34350
- "filters": {"id": {"op": "eq", "value": 278964}}
34403
+ "nodes": [{ "id": "mr", "entity": "MergeRequest", "node_ids": [507483917] }],
34404
+ "neighbors": {
34405
+ "direction": "both",
34406
+ "rel_types": ["AUTHORED", "REVIEWER", "IN_PROJECT", "CLOSES", "HAS_HEAD_PIPELINE"]
34351
34407
  },
34352
- "neighbors": {"node": "p", "direction": "incoming"},
34353
- "aggregations": [],
34354
- "path": {"type": "shortest", "from": "p", "to": "p", "max_depth": 1}
34408
+ "limit": 30
34355
34409
  }
34356
34410
 
34357
- Example traversal query:
34411
+ Example traversal (merged MRs in a project):
34358
34412
  {
34359
34413
  "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.`,
34414
+ "nodes": [
34415
+ { "id": "project", "entity": "Project", "filters": {"full_path": "gitlab-org/gitlab"} },
34416
+ { "id": "mr", "entity": "MergeRequest", "filters": {"state": "merged"},
34417
+ "columns": ["iid", "title", "merged_at"] }
34418
+ ],
34419
+ "relationships": [{ "type": "IN_PROJECT", "from": "mr", "to": "project" }],
34420
+ "limit": 25
34421
+ }
34422
+
34423
+ Notes:
34424
+ - node_ids are GitLab *internal* numeric ids (not IIDs). For an MR, get it via
34425
+ gitlab_get_merge_request (the "id" field). For epics, use work_item_id.
34426
+ - Traversal/aggregation queries must include at least one selective node.
34427
+ - aggregation adds an "aggregations" array (e.g. [{"count":"mr"}]) with optional
34428
+ "group_by"; path_finding adds a "path":{from,to,max_depth} object between two
34429
+ node selectors. Both use the same envelope; see gitlab_orbit_schema and the
34430
+ Orbit query-language docs for the full grammar.
34431
+ - Use gitlab_orbit_schema to discover entities and relationship types.
34432
+ Returns nodes and edges arrays.`,
34372
34433
  args: {
34373
34434
  // Using a JSON string rather than a structured zod schema because the Orbit query
34374
34435
  // format is still evolving. This allows flexibility as the API changes without
34375
34436
  // requiring schema updates for each iteration.
34376
34437
  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."
34438
+ 'JSON string containing the full query object. Must include "query_type" and a top-level "nodes" array of selectors (each pinned by "node_ids" or "filters"); add "relationships"/"neighbors"/"aggregations"/"path" per the query_type. See the tool description for the current DSL and worked examples.'
34378
34439
  ),
34379
34440
  limit: z18.number().optional().describe("Maximum number of results (default: 30)")
34380
34441
  },
@@ -34393,12 +34454,23 @@ Returns nodes array and edges array with relationship types.`,
34393
34454
  2
34394
34455
  );
34395
34456
  }
34457
+ if (typeof queryObj !== "object" || queryObj === null || Array.isArray(queryObj)) {
34458
+ return JSON.stringify(
34459
+ {
34460
+ error: "Invalid query parameter",
34461
+ suggestion: 'The query must be a JSON object with a "query_type" and a "nodes" array.'
34462
+ },
34463
+ null,
34464
+ 2
34465
+ );
34466
+ }
34467
+ if (args.limit !== void 0) {
34468
+ queryObj.limit = args.limit;
34469
+ }
34396
34470
  const requestBody = {
34397
- query: queryObj
34471
+ query: queryObj,
34472
+ response_format: "raw"
34398
34473
  };
34399
- if (args.limit) {
34400
- requestBody.limit = args.limit;
34401
- }
34402
34474
  try {
34403
34475
  const result = await client.fetch("POST", "/orbit/query", requestBody);
34404
34476
  if (result.code) {
@@ -34407,7 +34479,7 @@ Returns nodes array and edges array with relationship types.`,
34407
34479
  error: "Query compilation error",
34408
34480
  code: result.code,
34409
34481
  message: result.message,
34410
- suggestion: 'Check query structure. Node "id" is a variable name, actual IDs go in filters.'
34482
+ suggestion: 'Check query structure against the current DSL: top-level "nodes" array, pin ids with "node_ids", filters use {op: value}, and a "neighbors" query needs neighbors:{direction, rel_types} (max 10 rel_types).'
34411
34483
  },
34412
34484
  null,
34413
34485
  2
@@ -34455,7 +34527,7 @@ Common use cases:
34455
34527
 
34456
34528
  Note: Use GitLab internal numeric IDs (not IIDs).
34457
34529
  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).`,
34530
+ then use that ID (e.g., 377844873). For epics, use the work_item_id.`,
34459
34531
  args: {
34460
34532
  entity_type: z18.string().describe(
34461
34533
  "Type of the entity (e.g., Project, MergeRequest, User, Pipeline, Commit, WorkItem, Group)"
@@ -34464,33 +34536,38 @@ then use that ID (e.g., 377844873).`,
34464
34536
  "GitLab internal numeric ID (not IID). Get this from REST/GraphQL API responses."
34465
34537
  ),
34466
34538
  direction: z18.enum(["incoming", "outgoing", "both"]).optional().describe("Direction of relationships to traverse (default: both)"),
34539
+ rel_types: z18.array(z18.string()).optional().describe(
34540
+ 'Relationship types to follow. Required by the API to bound the lookup; if omitted, a sensible default set for the entity type is used. The API caps this at 10 per selector, so more than 10 entries are truncated to the first 10 and the response includes a "warnings" field. See gitlab_orbit_schema for valid edge types.'
34541
+ ),
34467
34542
  limit: z18.number().optional().describe("Maximum number of neighbors to return (default: 30)")
34468
34543
  },
34469
34544
  execute: async (args, _ctx) => {
34470
34545
  const client = getGitLabClient();
34471
34546
  const direction = args.direction || "both";
34547
+ const requestedRelTypes = args.rel_types?.length ? args.rel_types : orbitRelTypes(args.entity_type);
34548
+ const relTypes = requestedRelTypes.slice(0, 10);
34549
+ const warnings = requestedRelTypes.length > relTypes.length ? [`rel_types truncated to 10 (got ${requestedRelTypes.length})`] : [];
34472
34550
  const queryObj = {
34473
34551
  query_type: "neighbors",
34474
- node: {
34475
- entity: args.entity_type,
34476
- id: "n",
34477
- filters: {
34478
- id: { op: "eq", value: args.entity_id }
34552
+ nodes: [
34553
+ {
34554
+ id: "n",
34555
+ entity: args.entity_type,
34556
+ node_ids: [args.entity_id]
34479
34557
  }
34480
- },
34558
+ ],
34481
34559
  neighbors: {
34482
- node: "n",
34483
- direction
34484
- },
34485
- aggregations: [],
34486
- path: { type: "shortest", from: "n", to: "n", max_depth: 1 }
34560
+ direction,
34561
+ rel_types: relTypes
34562
+ }
34487
34563
  };
34564
+ if (args.limit !== void 0) {
34565
+ queryObj.limit = args.limit;
34566
+ }
34488
34567
  const requestBody = {
34489
- query: queryObj
34568
+ query: queryObj,
34569
+ response_format: "raw"
34490
34570
  };
34491
- if (args.limit) {
34492
- requestBody.limit = args.limit;
34493
- }
34494
34571
  try {
34495
34572
  const result = await client.fetch("POST", "/orbit/query", requestBody);
34496
34573
  if (result.code) {
@@ -34500,13 +34577,14 @@ then use that ID (e.g., 377844873).`,
34500
34577
  code: result.code,
34501
34578
  message: result.message,
34502
34579
  entity_type: args.entity_type,
34503
- entity_id: args.entity_id
34580
+ entity_id: args.entity_id,
34581
+ ...warnings.length > 0 && { warnings }
34504
34582
  },
34505
34583
  null,
34506
34584
  2
34507
34585
  );
34508
34586
  }
34509
- return JSON.stringify(result, null, 2);
34587
+ return JSON.stringify(warnings.length > 0 ? { ...result, warnings } : result, null, 2);
34510
34588
  } catch (error45) {
34511
34589
  const errorMessage = error45 instanceof Error ? error45.message : String(error45);
34512
34590
  if (errorMessage.includes("404") || errorMessage.includes("not found")) {
@@ -34590,7 +34668,7 @@ async function main() {
34590
34668
  `);
34591
34669
  process.exit(1);
34592
34670
  }
34593
- const version2 = true ? "3.0.0" : "0.0.0";
34671
+ const version2 = true ? "3.0.1" : "0.0.0";
34594
34672
  const server = new McpServer({ name: "gitlab", version: version2 });
34595
34673
  adaptToolsToMcp(server, mcpTools);
34596
34674
  const transport = new StdioServerTransport();