gitlab-mcp 1.2.1 → 1.3.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/README.md +30 -27
- package/dist/config/dotenv.js +6 -2
- package/dist/config/dotenv.js.map +1 -1
- package/dist/config/env.d.ts +3 -1
- package/dist/config/env.js +15 -1
- package/dist/config/env.js.map +1 -1
- package/dist/http-app.js +10 -3
- package/dist/http-app.js.map +1 -1
- package/dist/http.js +5 -4
- package/dist/http.js.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/auth-context.d.ts +2 -1
- package/dist/lib/auth-context.js.map +1 -1
- package/dist/lib/gitlab-client.d.ts +42 -8
- package/dist/lib/gitlab-client.js +380 -42
- package/dist/lib/gitlab-client.js.map +1 -1
- package/dist/lib/network.js +12 -6
- package/dist/lib/network.js.map +1 -1
- package/dist/lib/oauth-scopes.d.ts +2 -0
- package/dist/lib/oauth-scopes.js +16 -0
- package/dist/lib/oauth-scopes.js.map +1 -0
- package/dist/lib/regex.d.ts +5 -0
- package/dist/lib/regex.js +111 -0
- package/dist/lib/regex.js.map +1 -0
- package/dist/lib/request-runtime.js +24 -11
- package/dist/lib/request-runtime.js.map +1 -1
- package/dist/tools/gitlab.js +193 -3
- package/dist/tools/gitlab.js.map +1 -1
- package/dist/types/auth.d.ts +1 -0
- package/dist/types/auth.js +2 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/types/context.d.ts +1 -0
- package/docs/architecture.md +1 -1
- package/docs/authentication.md +4 -1
- package/docs/configuration.md +23 -21
- package/docs/deployment.md +2 -0
- package/docs/mcp-integration-testing-best-practices.md +381 -730
- package/docs/tools.md +24 -14
- package/package.json +1 -1
package/dist/tools/gitlab.js
CHANGED
|
@@ -12,6 +12,13 @@ const optionalNumberArray = z.preprocess((value) => (value === null ? undefined
|
|
|
12
12
|
const optionalStringOrNumber = z.preprocess((value) => (value === null ? undefined : value), z.union([z.string(), z.number()]).optional());
|
|
13
13
|
const optionalStringOrStringArray = z.preprocess((value) => (value === null ? undefined : value), z.union([z.string(), z.array(z.string())]).optional());
|
|
14
14
|
const optionalRecord = z.preprocess((value) => (value === null ? undefined : value), z.record(z.string(), z.unknown()).optional());
|
|
15
|
+
const pipelineInputValueSchema = z.union([
|
|
16
|
+
z.string(),
|
|
17
|
+
z.number(),
|
|
18
|
+
z.boolean(),
|
|
19
|
+
z.array(z.union([z.string(), z.number(), z.boolean()]))
|
|
20
|
+
]);
|
|
21
|
+
const optionalPipelineInputsRecord = z.preprocess((value) => (value === null ? undefined : value), z.record(z.string(), pipelineInputValueSchema).optional());
|
|
15
22
|
const paginationShape = {
|
|
16
23
|
page: optionalNumber,
|
|
17
24
|
per_page: optionalNumber
|
|
@@ -29,6 +36,9 @@ export function registerGitLabTools(server, context) {
|
|
|
29
36
|
if (!enabledNames.has(definition.name)) {
|
|
30
37
|
continue;
|
|
31
38
|
}
|
|
39
|
+
if (definition.requiresLocalFileTools && !context.allowLocalFileTools) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
32
42
|
if (disableGraphqlTools && isGraphqlToolName(definition.name)) {
|
|
33
43
|
continue;
|
|
34
44
|
}
|
|
@@ -526,7 +536,7 @@ function getGitLabToolDefinitions() {
|
|
|
526
536
|
},
|
|
527
537
|
handler: async (args, context) => {
|
|
528
538
|
const projectId = resolveProjectId(args, context, false);
|
|
529
|
-
const query = toQuery(omit(args, ["project_id"]));
|
|
539
|
+
const query = toQuery(cleanMergeRequestListArgs(omit(args, ["project_id"])));
|
|
530
540
|
if (projectId) {
|
|
531
541
|
return context.gitlab.listMergeRequests(projectId, { query });
|
|
532
542
|
}
|
|
@@ -816,6 +826,17 @@ function getGitLabToolDefinitions() {
|
|
|
816
826
|
},
|
|
817
827
|
handler: async (args, context) => context.gitlab.getMergeRequestApprovalState(resolveProjectId(args, context, true), getString(args, "merge_request_iid"))
|
|
818
828
|
},
|
|
829
|
+
{
|
|
830
|
+
name: "gitlab_get_merge_request_conflicts",
|
|
831
|
+
title: "Get Merge Request Conflicts",
|
|
832
|
+
description: "Get conflict details for MR.",
|
|
833
|
+
mutating: false,
|
|
834
|
+
inputSchema: {
|
|
835
|
+
project_id: z.string().optional(),
|
|
836
|
+
merge_request_iid: z.string().min(1)
|
|
837
|
+
},
|
|
838
|
+
handler: async (args, context) => context.gitlab.getMergeRequestConflicts(resolveProjectId(args, context, true), getString(args, "merge_request_iid"))
|
|
839
|
+
},
|
|
819
840
|
{
|
|
820
841
|
name: "gitlab_list_merge_request_discussions",
|
|
821
842
|
title: "List Merge Request Discussions",
|
|
@@ -1501,6 +1522,71 @@ function getGitLabToolDefinitions() {
|
|
|
1501
1522
|
},
|
|
1502
1523
|
handler: async (args, context) => context.gitlab.getPipeline(resolveProjectId(args, context, true), getString(args, "pipeline_id"))
|
|
1503
1524
|
},
|
|
1525
|
+
{
|
|
1526
|
+
name: "gitlab_list_deployments",
|
|
1527
|
+
title: "List Deployments",
|
|
1528
|
+
description: "List deployments in a project.",
|
|
1529
|
+
mutating: false,
|
|
1530
|
+
requiresFeature: "pipeline",
|
|
1531
|
+
inputSchema: {
|
|
1532
|
+
project_id: z.string().optional(),
|
|
1533
|
+
environment: optionalString,
|
|
1534
|
+
ref: optionalString,
|
|
1535
|
+
sha: optionalString,
|
|
1536
|
+
status: optionalString,
|
|
1537
|
+
updated_after: optionalString,
|
|
1538
|
+
updated_before: optionalString,
|
|
1539
|
+
order_by: z
|
|
1540
|
+
.enum(["id", "iid", "created_at", "updated_at", "ref", "status", "environment"])
|
|
1541
|
+
.optional(),
|
|
1542
|
+
sort: z.enum(["asc", "desc"]).optional(),
|
|
1543
|
+
...paginationShape
|
|
1544
|
+
},
|
|
1545
|
+
handler: async (args, context) => context.gitlab.listDeployments(resolveProjectId(args, context, true), {
|
|
1546
|
+
query: toQuery(omit(args, ["project_id"]))
|
|
1547
|
+
})
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
name: "gitlab_get_deployment",
|
|
1551
|
+
title: "Get Deployment",
|
|
1552
|
+
description: "Get one deployment by ID.",
|
|
1553
|
+
mutating: false,
|
|
1554
|
+
requiresFeature: "pipeline",
|
|
1555
|
+
inputSchema: {
|
|
1556
|
+
project_id: z.string().optional(),
|
|
1557
|
+
deployment_id: z.string().min(1)
|
|
1558
|
+
},
|
|
1559
|
+
handler: async (args, context) => context.gitlab.getDeployment(resolveProjectId(args, context, true), getString(args, "deployment_id"))
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: "gitlab_list_environments",
|
|
1563
|
+
title: "List Environments",
|
|
1564
|
+
description: "List environments in a project.",
|
|
1565
|
+
mutating: false,
|
|
1566
|
+
requiresFeature: "pipeline",
|
|
1567
|
+
inputSchema: {
|
|
1568
|
+
project_id: z.string().optional(),
|
|
1569
|
+
name: optionalString,
|
|
1570
|
+
search: optionalString,
|
|
1571
|
+
states: z.enum(["available", "stopped"]).optional(),
|
|
1572
|
+
...paginationShape
|
|
1573
|
+
},
|
|
1574
|
+
handler: async (args, context) => context.gitlab.listEnvironments(resolveProjectId(args, context, true), {
|
|
1575
|
+
query: toQuery(omit(args, ["project_id"]))
|
|
1576
|
+
})
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
name: "gitlab_get_environment",
|
|
1580
|
+
title: "Get Environment",
|
|
1581
|
+
description: "Get one environment by ID.",
|
|
1582
|
+
mutating: false,
|
|
1583
|
+
requiresFeature: "pipeline",
|
|
1584
|
+
inputSchema: {
|
|
1585
|
+
project_id: z.string().optional(),
|
|
1586
|
+
environment_id: z.string().min(1)
|
|
1587
|
+
},
|
|
1588
|
+
handler: async (args, context) => context.gitlab.getEnvironment(resolveProjectId(args, context, true), getString(args, "environment_id"))
|
|
1589
|
+
},
|
|
1504
1590
|
{
|
|
1505
1591
|
name: "gitlab_list_pipeline_jobs",
|
|
1506
1592
|
title: "List Pipeline Jobs",
|
|
@@ -1580,6 +1666,74 @@ function getGitLabToolDefinitions() {
|
|
|
1580
1666
|
},
|
|
1581
1667
|
handler: async (args, context) => context.gitlab.getPipelineJobOutput(resolveProjectId(args, context, true), getString(args, "job_id"))
|
|
1582
1668
|
},
|
|
1669
|
+
{
|
|
1670
|
+
name: "gitlab_list_job_artifacts",
|
|
1671
|
+
title: "List Job Artifacts",
|
|
1672
|
+
description: "List files and directories inside a job artifacts archive.",
|
|
1673
|
+
mutating: false,
|
|
1674
|
+
requiresFeature: "pipeline",
|
|
1675
|
+
inputSchema: {
|
|
1676
|
+
project_id: z.string().optional(),
|
|
1677
|
+
job_id: z.string().min(1),
|
|
1678
|
+
path: optionalString,
|
|
1679
|
+
recursive: optionalBoolean
|
|
1680
|
+
},
|
|
1681
|
+
handler: async (args, context) => context.gitlab.listJobArtifacts(resolveProjectId(args, context, true), getString(args, "job_id"), { query: toQuery(omit(args, ["project_id", "job_id"])) })
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
name: "gitlab_download_job_artifacts",
|
|
1685
|
+
title: "Download Job Artifacts",
|
|
1686
|
+
description: "Download the full job artifacts archive as base64 content.",
|
|
1687
|
+
mutating: false,
|
|
1688
|
+
requiresFeature: "pipeline",
|
|
1689
|
+
inputSchema: {
|
|
1690
|
+
project_id: z.string().optional(),
|
|
1691
|
+
job_id: z.string().min(1)
|
|
1692
|
+
},
|
|
1693
|
+
handler: async (args, context) => context.gitlab.downloadJobArtifacts(resolveProjectId(args, context, true), getString(args, "job_id"))
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
name: "gitlab_download_job_artifacts_local",
|
|
1697
|
+
title: "Download Job Artifacts Local",
|
|
1698
|
+
description: "Download the full job artifacts archive to a local directory.",
|
|
1699
|
+
mutating: true,
|
|
1700
|
+
requiresFeature: "pipeline",
|
|
1701
|
+
requiresLocalFileTools: true,
|
|
1702
|
+
inputSchema: {
|
|
1703
|
+
project_id: z.string().optional(),
|
|
1704
|
+
job_id: z.string().min(1),
|
|
1705
|
+
local_path: optionalString
|
|
1706
|
+
},
|
|
1707
|
+
handler: async (args, context) => context.gitlab.saveJobArtifacts(resolveProjectId(args, context, true), getString(args, "job_id"), getOptionalString(args, "local_path"))
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
name: "gitlab_get_job_artifact_file",
|
|
1711
|
+
title: "Get Job Artifact File",
|
|
1712
|
+
description: "Return one file from a job artifacts archive as inline UTF-8 or base64 content.",
|
|
1713
|
+
mutating: false,
|
|
1714
|
+
requiresFeature: "pipeline",
|
|
1715
|
+
inputSchema: {
|
|
1716
|
+
project_id: z.string().optional(),
|
|
1717
|
+
job_id: z.string().min(1),
|
|
1718
|
+
artifact_path: z.string().min(1)
|
|
1719
|
+
},
|
|
1720
|
+
handler: async (args, context) => context.gitlab.getJobArtifactFile(resolveProjectId(args, context, true), getString(args, "job_id"), getString(args, "artifact_path"))
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
name: "gitlab_get_job_artifact_file_local",
|
|
1724
|
+
title: "Get Job Artifact File Local",
|
|
1725
|
+
description: "Save one file from a job artifacts archive to a local directory.",
|
|
1726
|
+
mutating: true,
|
|
1727
|
+
requiresFeature: "pipeline",
|
|
1728
|
+
requiresLocalFileTools: true,
|
|
1729
|
+
inputSchema: {
|
|
1730
|
+
project_id: z.string().optional(),
|
|
1731
|
+
job_id: z.string().min(1),
|
|
1732
|
+
artifact_path: z.string().min(1),
|
|
1733
|
+
local_path: optionalString
|
|
1734
|
+
},
|
|
1735
|
+
handler: async (args, context) => context.gitlab.saveJobArtifactFile(resolveProjectId(args, context, true), getString(args, "job_id"), getString(args, "artifact_path"), getOptionalString(args, "local_path"))
|
|
1736
|
+
},
|
|
1583
1737
|
{
|
|
1584
1738
|
name: "gitlab_create_pipeline",
|
|
1585
1739
|
title: "Create Pipeline",
|
|
@@ -1589,6 +1743,7 @@ function getGitLabToolDefinitions() {
|
|
|
1589
1743
|
inputSchema: {
|
|
1590
1744
|
project_id: z.string().optional(),
|
|
1591
1745
|
ref: z.string().min(1),
|
|
1746
|
+
inputs: optionalPipelineInputsRecord,
|
|
1592
1747
|
variables: z
|
|
1593
1748
|
.array(z.object({
|
|
1594
1749
|
key: z.string(),
|
|
@@ -1599,7 +1754,8 @@ function getGitLabToolDefinitions() {
|
|
|
1599
1754
|
},
|
|
1600
1755
|
handler: async (args, context) => context.gitlab.createPipeline(resolveProjectId(args, context, true), {
|
|
1601
1756
|
ref: getString(args, "ref"),
|
|
1602
|
-
|
|
1757
|
+
inputs: getOptionalPipelineInputsRecord(args, "inputs"),
|
|
1758
|
+
variables: getOptionalArray(args, "variables")
|
|
1603
1759
|
})
|
|
1604
1760
|
},
|
|
1605
1761
|
{
|
|
@@ -2516,8 +2672,11 @@ function getOptionalNumber(args, key) {
|
|
|
2516
2672
|
}
|
|
2517
2673
|
return value;
|
|
2518
2674
|
}
|
|
2519
|
-
function
|
|
2675
|
+
function getOptionalArray(args, key) {
|
|
2520
2676
|
const value = args[key];
|
|
2677
|
+
if (value === undefined) {
|
|
2678
|
+
return undefined;
|
|
2679
|
+
}
|
|
2521
2680
|
if (!Array.isArray(value)) {
|
|
2522
2681
|
throw new Error(`'${key}' must be array`);
|
|
2523
2682
|
}
|
|
@@ -2543,6 +2702,37 @@ function getOptionalRecord(args, key) {
|
|
|
2543
2702
|
}
|
|
2544
2703
|
return value;
|
|
2545
2704
|
}
|
|
2705
|
+
function getOptionalPipelineInputsRecord(args, key) {
|
|
2706
|
+
const value = getOptionalRecord(args, key);
|
|
2707
|
+
if (!value) {
|
|
2708
|
+
return undefined;
|
|
2709
|
+
}
|
|
2710
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
2711
|
+
if (!pipelineInputValueSchema.safeParse(entryValue).success) {
|
|
2712
|
+
throw new Error(`'${key}.${entryKey}' must be a string, number, boolean, or array of primitive values`);
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return value;
|
|
2716
|
+
}
|
|
2717
|
+
function cleanMergeRequestListArgs(args) {
|
|
2718
|
+
const cleanedArgs = { ...args };
|
|
2719
|
+
if (hasValue(cleanedArgs.author_username)) {
|
|
2720
|
+
delete cleanedArgs.author_id;
|
|
2721
|
+
}
|
|
2722
|
+
if (hasValue(cleanedArgs.assignee_username)) {
|
|
2723
|
+
delete cleanedArgs.assignee_id;
|
|
2724
|
+
}
|
|
2725
|
+
if (hasValue(cleanedArgs.reviewer_username)) {
|
|
2726
|
+
delete cleanedArgs.reviewer_id;
|
|
2727
|
+
}
|
|
2728
|
+
return cleanedArgs;
|
|
2729
|
+
}
|
|
2730
|
+
function hasValue(value) {
|
|
2731
|
+
if (typeof value === "string") {
|
|
2732
|
+
return value.length > 0;
|
|
2733
|
+
}
|
|
2734
|
+
return value !== undefined && value !== null;
|
|
2735
|
+
}
|
|
2546
2736
|
function requireArrayValue(items, index, errorMessage) {
|
|
2547
2737
|
const value = items[index];
|
|
2548
2738
|
if (value === undefined) {
|