@zereight/mcp-gitlab 2.3.8-next.144 → 2.3.8-next.145

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.ko.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  `@zereight/mcp-gitlab`의 권장 로컬 PAT 설치입니다. 명령 인자는 [Claude Code](./docs/clients/claude-code.md)와 [Codex](./docs/clients/codex.md) 가이드와 같습니다.
12
12
 
13
- `zereight-mcp-gitlab` 바이너리를 먼저 설치하세요. `npm install -g @zereight/mcp-gitlab`, 또는 Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` 다음 `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`.
13
+ `zereight-mcp-gitlab` 바이너리를 먼저 설치하세요. `npm install -g @zereight/mcp-gitlab`, 또는 Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` 다음 `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`. 공식 MCP Registry 항목: [`io.github.zereight/gitlab-mcp`](https://registry.modelcontextprotocol.io/?q=io.github.zereight%2Fgitlab-mcp). 한 줄 설치: `npx -y @zereight/mcp-gitlab@latest`.
14
14
 
15
15
  토큰을 화면에 표시하지 않고 읽은 다음 export 하세요. 셸 기록에는 변수 이름만 남습니다.
16
16
 
@@ -202,7 +202,7 @@ CLI 인자는 환경 변수보다 우선합니다.
202
202
 
203
203
  `zereight-mcp-gitlab auth`는 MCP 서버 플래그가 아니라 서브커맨드입니다. GitLab device flow를 실행한 뒤 종료합니다. [CLI 인자](./docs/getting-started/cli-arguments.md#auth)를 참고하세요.
204
204
 
205
- 같은 바이너리는 `gh` 스타일 GitLab CLI이기도 합니다 (`tool <name>` 또는 `mr list` 같은 curated 명령). **MCP 등록은 필요 없습니다** — PAT를 설정하거나, `auth`를 실행한 뒤 `GITLAB_USE_OAUTH=true`를 설정하면 됩니다: [MCP 없이 CLI 사용하기](./docs/getting-started/cli-without-mcp.md). 전체 페이로드는 터미널에서 이 CLI로 읽습니다. MCP 응답은 대화에 남습니다. `GITLAB_MCP_COMPACT_RESULTS=true`(또는 `--compact-results`)를 켜면 큰 도구 응답이 미리보기와 CLI 명령으로 바뀝니다. Human CLI 자체는 compact 되지 않습니다. permission mode, toolsets, denied-tools regex, tool-policy는 MCP와 같습니다. destructive 도구와 `GITLAB_TOOL_POLICY_APPROVE` 도구는 `--yes`가 필요합니다. [Human CLI](./docs/getting-started/cli-arguments.md#human-cli)를 참고하세요.
205
+ 같은 바이너리는 `gh` 스타일 GitLab CLI이기도 합니다 (`tool <name>` 또는 `mr list` 같은 curated 명령). **MCP 등록은 필요 없습니다** — PAT를 설정하거나, `auth`를 실행한 뒤 `GITLAB_USE_OAUTH=true`를 설정하면 됩니다: [MCP 없이 CLI 사용하기](./docs/getting-started/cli-without-mcp.md). `setup --cursor` / `setup --claude`는 클라이언트 설정을 출력하고, `doctor`는 토큰·API URL·toolset·버전을 점검합니다. 전체 페이로드는 터미널에서 이 CLI로 읽습니다. MCP 응답은 대화에 남습니다. `GITLAB_MCP_COMPACT_RESULTS=true`(또는 `--compact-results`)를 켜면 큰 도구 응답이 미리보기와 CLI 명령으로 바뀝니다. Human CLI 자체는 compact 되지 않습니다. permission mode, toolsets, denied-tools regex, tool-policy는 MCP와 같습니다. destructive 도구와 `GITLAB_TOOL_POLICY_APPROVE` 도구는 `--yes`가 필요합니다. [Human CLI](./docs/getting-started/cli-arguments.md#human-cli)를 참고하세요.
206
206
 
207
207
  > **세밀한 도구 필터링:** `GITLAB_PERMISSION_MODE=modify`로 생성/수정은 허용하고 모든 삭제 도구와
208
208
  > 파괴적인 중단(teardown) 도구(`cancel_pipeline`, `cancel_pipeline_job`, `stop_environment`,
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  Install `@zereight/mcp-gitlab` with the recommended local personal-access-token setup. These commands use the same arguments as the [Claude Code](./docs/clients/claude-code.md) and [Codex](./docs/clients/codex.md) guides.
17
17
 
18
- Install the `zereight-mcp-gitlab` binary first with `npm install -g @zereight/mcp-gitlab`, or Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` and `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`.
18
+ Install the `zereight-mcp-gitlab` binary first with `npm install -g @zereight/mcp-gitlab`, or Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` and `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`. Official MCP Registry listing: [`io.github.zereight/gitlab-mcp`](https://registry.modelcontextprotocol.io/?q=io.github.zereight%2Fgitlab-mcp). One-liner: `npx -y @zereight/mcp-gitlab@latest`.
19
19
 
20
20
  Read the token without echo, then export it. The shell history keeps the variable name.
21
21
 
@@ -207,7 +207,7 @@ CLI arguments take precedence over environment variables.
207
207
 
208
208
  `zereight-mcp-gitlab auth` is a subcommand (not an MCP server flag). It runs GitLab device flow and exits. See [CLI Arguments](./docs/getting-started/cli-arguments.md#auth).
209
209
 
210
- The same binary is also a `gh`-style GitLab CLI (`tool <name>` or curated forms such as `mr list`). **No MCP registration is needed** — set a PAT, or run `auth` and set `GITLAB_USE_OAUTH=true`: see [CLI without MCP](./docs/getting-started/cli-without-mcp.md). Use it for the full payload in a terminal. MCP replies stay in the chat unless you set `GITLAB_MCP_COMPACT_RESULTS=true` (or `--compact-results`), which replaces oversized tool replies with a preview and a CLI command. Human CLI is never compacted. Same permission mode, toolsets, denied-tools regex, and tool-policy filters. Destructive tools and `GITLAB_TOOL_POLICY_APPROVE` tools need `--yes`. See [Human CLI](./docs/getting-started/cli-arguments.md#human-cli).
210
+ The same binary is also a `gh`-style GitLab CLI (`tool <name>` or curated forms such as `mr list`). **No MCP registration is needed** — set a PAT, or run `auth` and set `GITLAB_USE_OAUTH=true`: see [CLI without MCP](./docs/getting-started/cli-without-mcp.md). `setup --cursor` / `setup --claude` prints client config; `doctor` checks token, API URL, toolsets, and version. Use it for the full payload in a terminal. MCP replies stay in the chat unless you set `GITLAB_MCP_COMPACT_RESULTS=true` (or `--compact-results`), which replaces oversized tool replies with a preview and a CLI command. Human CLI is never compacted. Same permission mode, toolsets, denied-tools regex, and tool-policy filters. Destructive tools and `GITLAB_TOOL_POLICY_APPROVE` tools need `--yes`. See [Human CLI](./docs/getting-started/cli-arguments.md#human-cli).
211
211
 
212
212
  > **Fine-grained tool filtering:** use `GITLAB_PERMISSION_MODE=modify` to allow create/update while
213
213
  > blocking every delete tool and the destructive teardown tools (`cancel_pipeline`,
package/README.pt-BR.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  Instalação local recomendada com PAT para `@zereight/mcp-gitlab`. Os argumentos são os mesmos dos guias de [Claude Code](./docs/clients/claude-code.md) e [Codex](./docs/clients/codex.md).
12
12
 
13
- Instale o binário `zereight-mcp-gitlab` antes. `npm install -g @zereight/mcp-gitlab`, ou Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` e `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`.
13
+ Instale o binário `zereight-mcp-gitlab` antes. `npm install -g @zereight/mcp-gitlab`, ou Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` e `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`. Listagem oficial no MCP Registry: [`io.github.zereight/gitlab-mcp`](https://registry.modelcontextprotocol.io/?q=io.github.zereight%2Fgitlab-mcp). Instalação em uma linha: `npx -y @zereight/mcp-gitlab@latest`.
14
14
 
15
15
  Leia o token sem eco e exporte. O histórico do shell fica só com o nome da variável.
16
16
 
@@ -202,7 +202,7 @@ Os argumentos de CLI têm precedência sobre as variáveis de ambiente.
202
202
 
203
203
  `zereight-mcp-gitlab auth` é um subcomando, não uma flag do servidor MCP. Ele executa o device flow do GitLab e encerra. Veja [Argumentos de CLI](./docs/getting-started/cli-arguments.md#auth).
204
204
 
205
- O mesmo binário também é uma CLI do GitLab no estilo `gh` (`tool <name>` ou formas curadas como `mr list`). **Não é necessário registrar o MCP** — defina um PAT, ou execute `auth` e defina `GITLAB_USE_OAUTH=true`: veja [CLI sem MCP](./docs/getting-started/cli-without-mcp.md). Use-o para ver a carga completa no terminal. As respostas do MCP permanecem no chat, a menos que você defina `GITLAB_MCP_COMPACT_RESULTS=true` (ou `--compact-results`), o que substitui respostas grandes de ferramentas por uma prévia e um comando CLI. A CLI humana nunca é compactada. Os mesmos filtros de permission mode, toolsets, denied-tools regex e tool-policy se aplicam. Ferramentas destrutivas e ferramentas com `GITLAB_TOOL_POLICY_APPROVE` exigem `--yes`. Consulte [CLI Humana](./docs/getting-started/cli-arguments.md#human-cli).
205
+ O mesmo binário também é uma CLI do GitLab no estilo `gh` (`tool <name>` ou formas curadas como `mr list`). **Não é necessário registrar o MCP** — defina um PAT, ou execute `auth` e defina `GITLAB_USE_OAUTH=true`: veja [CLI sem MCP](./docs/getting-started/cli-without-mcp.md). `setup --cursor` / `setup --claude` imprime a configuração do cliente; `doctor` verifica token, URL da API, toolsets e versão. Use-o para ver a carga completa no terminal. As respostas do MCP permanecem no chat, a menos que você defina `GITLAB_MCP_COMPACT_RESULTS=true` (ou `--compact-results`), o que substitui respostas grandes de ferramentas por uma prévia e um comando CLI. A CLI humana nunca é compactada. Os mesmos filtros de permission mode, toolsets, denied-tools regex e tool-policy se aplicam. Ferramentas destrutivas e ferramentas com `GITLAB_TOOL_POLICY_APPROVE` exigem `--yes`. Consulte [CLI Humana](./docs/getting-started/cli-arguments.md#human-cli).
206
206
 
207
207
  > **Filtragem granular de ferramentas:** use `GITLAB_PERMISSION_MODE=modify` para permitir criação/atualização enquanto
208
208
  > bloqueia todas as ferramentas de exclusão e as ferramentas destrutivas de teardown (`cancel_pipeline`,
package/README.zh-CN.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  下面是 `@zereight/mcp-gitlab` 的推荐本地 PAT 安装。命令参数与 [Claude Code](./docs/clients/claude-code.md) 和 [Codex](./docs/clients/codex.md) 指南相同。
12
12
 
13
- 先安装 `zereight-mcp-gitlab` 二进制。`npm install -g @zereight/mcp-gitlab`,或 Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` 然后 `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`。
13
+ 先安装 `zereight-mcp-gitlab` 二进制。`npm install -g @zereight/mcp-gitlab`,或 Homebrew `brew tap zereight/gitlab-mcp https://github.com/zereight/gitlab-mcp` 然后 `brew install zereight/gitlab-mcp/zereight-mcp-gitlab`。官方 MCP Registry 条目:[`io.github.zereight/gitlab-mcp`](https://registry.modelcontextprotocol.io/?q=io.github.zereight%2Fgitlab-mcp)。一键安装:`npx -y @zereight/mcp-gitlab@latest`。
14
14
 
15
15
  先不回显地读取令牌,再 export。shell 历史里只保留变量名。
16
16
 
@@ -202,7 +202,7 @@ CLI 参数优先于环境变量。
202
202
 
203
203
  `zereight-mcp-gitlab auth` 是子命令,不是 MCP 服务器参数。它运行 GitLab device flow 后退出。参见 [CLI 参数](./docs/getting-started/cli-arguments.md#auth)。
204
204
 
205
- 同一二进制也是 `gh` 风格的 GitLab CLI(`tool <name>`,或 `mr list` 这类 curated 命令)。**无需注册 MCP**——设置 PAT,或运行 `auth` 后设置 `GITLAB_USE_OAUTH=true`:参见 [无需 MCP 使用 CLI](./docs/getting-started/cli-without-mcp.md)。完整结果在终端用 CLI 查看。MCP 回复仍进入对话。设置 `GITLAB_MCP_COMPACT_RESULTS=true`(或 `--compact-results`)后,过大的工具回复会变成预览加上 CLI 命令。Human CLI 本身不会被 compact。permission mode、toolsets、denied-tools regex 和 tool-policy 与 MCP 相同。破坏性工具以及 `GITLAB_TOOL_POLICY_APPROVE` 工具需要 `--yes`。参见 [Human CLI](./docs/getting-started/cli-arguments.md#human-cli)。
205
+ 同一二进制也是 `gh` 风格的 GitLab CLI(`tool <name>`,或 `mr list` 这类 curated 命令)。**无需注册 MCP**——设置 PAT,或运行 `auth` 后设置 `GITLAB_USE_OAUTH=true`:参见 [无需 MCP 使用 CLI](./docs/getting-started/cli-without-mcp.md)。`setup --cursor` / `setup --claude` 会打印客户端配置;`doctor` 检查 token、API URL、toolset 和版本。完整结果在终端用 CLI 查看。MCP 回复仍进入对话。设置 `GITLAB_MCP_COMPACT_RESULTS=true`(或 `--compact-results`)后,过大的工具回复会变成预览加上 CLI 命令。Human CLI 本身不会被 compact。permission mode、toolsets、denied-tools regex 和 tool-policy 与 MCP 相同。破坏性工具以及 `GITLAB_TOOL_POLICY_APPROVE` 工具需要 `--yes`。参见 [Human CLI](./docs/getting-started/cli-arguments.md#human-cli)。
206
206
 
207
207
  > **细粒度工具过滤:**使用 `GITLAB_PERMISSION_MODE=modify` 允许创建/更新,同时阻止所有删除工具以及
208
208
  > 破坏性的拆除(teardown)工具(`cancel_pipeline`、`cancel_pipeline_job`、`stop_environment`、
package/build/cli/args.js CHANGED
@@ -7,7 +7,17 @@ export class CliUsageError extends Error {
7
7
  }
8
8
  }
9
9
  const SCRIPT_PATH_PATTERN = /\.(cjs|mjs|js|cts|mts|ts)$/;
10
- const FLAGS_WITHOUT_VALUE = new Set(["help", "yes", ...BOOLEAN_CLI_FLAG_NAMES]);
10
+ const FLAGS_WITHOUT_VALUE = new Set([
11
+ "help",
12
+ "yes",
13
+ "cursor",
14
+ "claude",
15
+ "write",
16
+ "skill",
17
+ "probe",
18
+ "oauth",
19
+ ...BOOLEAN_CLI_FLAG_NAMES,
20
+ ]);
11
21
  const GLOBAL_FLAG_NAMES = new Set([
12
22
  "help",
13
23
  "yes",
@@ -199,7 +199,11 @@ export function canonicalGroup(name) {
199
199
  return GROUP_ALIASES[name];
200
200
  }
201
201
  export function isKnownCliCommand(name) {
202
- return name === "auth" || name === "tool" || canonicalGroup(name) !== undefined;
202
+ return (name === "auth" ||
203
+ name === "tool" ||
204
+ name === "setup" ||
205
+ name === "doctor" ||
206
+ canonicalGroup(name) !== undefined);
203
207
  }
204
208
  export function findCuratedCommand(group, action) {
205
209
  const canonical = canonicalGroup(group);
@@ -0,0 +1,104 @@
1
+ import { SERVER_VERSION } from "../server/version.js";
2
+ export function doctorHelpText() {
3
+ return `zereight-mcp-gitlab doctor
4
+
5
+ Check token presence, API URL, permission mode, toolsets, and package version.
6
+ Does not print secret values.
7
+
8
+ Usage:
9
+ zereight-mcp-gitlab doctor [--probe]
10
+ zereight-mcp-gitlab doctor --help
11
+
12
+ Flags:
13
+ --probe GET {apiUrl}/user with the configured credentials (network)
14
+ `;
15
+ }
16
+ export function packageVersion() {
17
+ return SERVER_VERSION;
18
+ }
19
+ export function runDoctor(input) {
20
+ const checks = [
21
+ credentialCheck(input.credentials),
22
+ apiUrlCheck(input.apiUrl),
23
+ permissionModeCheck(input.permissionMode),
24
+ toolsetCheck(input.toolsetsRaw),
25
+ versionCheck(input.version),
26
+ ];
27
+ return formatDoctorReport(checks);
28
+ }
29
+ export async function runDoctorWithOptionalProbe(input) {
30
+ const report = runDoctor(input);
31
+ if (input.probe === undefined) {
32
+ return report;
33
+ }
34
+ const probe = await input.probe(input.apiUrl);
35
+ return formatDoctorReport([
36
+ ...report.checks,
37
+ { name: "gitlab_user", ok: probe.ok, detail: probe.detail },
38
+ ]);
39
+ }
40
+ export function formatDoctorReport(checks) {
41
+ const ok = checks.every(check => check.ok);
42
+ const lines = checks.map(check => `${check.ok ? "ok" : "fail"} ${check.name}: ${check.detail}`);
43
+ return {
44
+ checks,
45
+ ok,
46
+ text: `${lines.join("\n")}\n`,
47
+ };
48
+ }
49
+ function credentialCheck(credentials) {
50
+ const hasAny = credentials.hasPersonalAccessToken ||
51
+ credentials.hasJobToken ||
52
+ credentials.hasCookie ||
53
+ credentials.useOAuth;
54
+ return {
55
+ name: "credentials",
56
+ ok: hasAny,
57
+ detail: hasAny
58
+ ? "a token, job token, cookie, or OAuth is configured (value hidden)"
59
+ : "set GITLAB_PERSONAL_ACCESS_TOKEN or run `zereight-mcp-gitlab auth`",
60
+ };
61
+ }
62
+ function apiUrlCheck(apiUrl) {
63
+ try {
64
+ const parsed = new URL(apiUrl);
65
+ const hasApi = parsed.pathname.includes("/api/");
66
+ return {
67
+ name: "api_url",
68
+ ok: hasApi,
69
+ detail: hasApi
70
+ ? `${parsed.origin}${parsed.pathname}`
71
+ : `${apiUrl} is missing /api/v4 (use the API URL, not the web root)`,
72
+ };
73
+ }
74
+ catch {
75
+ return {
76
+ name: "api_url",
77
+ ok: false,
78
+ detail: `${apiUrl} is not a valid URL`,
79
+ };
80
+ }
81
+ }
82
+ function permissionModeCheck(mode) {
83
+ const ok = mode === "readonly" || mode === "modify" || mode === "full";
84
+ return {
85
+ name: "permission_mode",
86
+ ok,
87
+ detail: ok ? mode : `${mode} is not readonly, modify, or full`,
88
+ };
89
+ }
90
+ function toolsetCheck(raw) {
91
+ const value = raw === undefined || raw.trim() === "" ? "core (default)" : raw.trim();
92
+ return {
93
+ name: "toolsets",
94
+ ok: true,
95
+ detail: value,
96
+ };
97
+ }
98
+ function versionCheck(version) {
99
+ return {
100
+ name: "version",
101
+ ok: version !== "unknown",
102
+ detail: version,
103
+ };
104
+ }
package/build/cli/help.js CHANGED
@@ -8,6 +8,8 @@ export function globalHelpText() {
8
8
 
9
9
  Usage:
10
10
  ${BINARY} auth [options]
11
+ ${BINARY} setup --cursor|--claude [options]
12
+ ${BINARY} doctor [--probe]
11
13
  ${BINARY} tool <tool-name> [options]
12
14
  ${BINARY} <group> <action> [options]
13
15
  ${BINARY} --help
@@ -5,6 +5,8 @@ import { applyFlagAliases, argvHasHelp, CliUsageError, parseArgv, parseToolArgs,
5
5
  import { liveCliExposure } from "./exposure.js";
6
6
  import { canonicalGroup, findCuratedCommand, isKnownCliCommand, resolveScopedTool, } from "./curated.js";
7
7
  import { curatedHelpText, globalHelpText, groupHelpText, toolHelpText, toolIndexHelpText, } from "./help.js";
8
+ import { doctorHelpText } from "./doctor.js";
9
+ import { readSetupOptions, setupHelpText } from "./setup.js";
8
10
  export function isCliInvocation(argv) {
9
11
  if (argvHasHelp(argv)) {
10
12
  return true;
@@ -38,6 +40,24 @@ function resolveCliUnsafe(argv, permissionMode, exposure) {
38
40
  if (head === "auth") {
39
41
  return { kind: "auth" };
40
42
  }
43
+ if (head === "setup") {
44
+ if (action !== undefined) {
45
+ return { kind: "usage", message: `Unexpected extra arguments: ${parsed.positionals.slice(1).join(" ")}` };
46
+ }
47
+ if (parsed.help) {
48
+ return { kind: "help", text: setupHelpText() };
49
+ }
50
+ return { kind: "setup", options: readSetupOptions(parsed.toolFlags) };
51
+ }
52
+ if (head === "doctor") {
53
+ if (action !== undefined) {
54
+ return { kind: "usage", message: `Unexpected extra arguments: ${parsed.positionals.slice(1).join(" ")}` };
55
+ }
56
+ if (parsed.help) {
57
+ return { kind: "help", text: doctorHelpText() };
58
+ }
59
+ return { kind: "doctor", probe: parsed.toolFlags.probe === "true" };
60
+ }
41
61
  if (head === "tool") {
42
62
  if (extra !== undefined) {
43
63
  return { kind: "usage", message: `Unexpected extra arguments: ${parsed.positionals.slice(2).join(" ")}` };
@@ -0,0 +1,137 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { CliUsageError } from "./args.js";
4
+ const DEFAULT_API_URL = "https://gitlab.com/api/v4";
5
+ const SKILL_INSTALL = "npx skills add zereight/gitlab-mcp --skill gitlab-mcp-skill";
6
+ export function setupHelpText() {
7
+ return `zereight-mcp-gitlab setup
8
+
9
+ Print (or write) a local MCP client config. Tokens stay in the environment.
10
+
11
+ Usage:
12
+ zereight-mcp-gitlab setup --cursor [--oauth] [--write] [--dir PATH]
13
+ zereight-mcp-gitlab setup --claude [--oauth] [--write]
14
+ zereight-mcp-gitlab setup --help
15
+
16
+ Flags:
17
+ --cursor Cursor mcp.json snippet (default path .cursor/mcp.json)
18
+ --claude Claude Code one-liner (claude mcp add)
19
+ --oauth Use GITLAB_USE_OAUTH instead of a PAT
20
+ --write Write the Cursor file or print the Claude command as the install step
21
+ --skill Also print the Agent Skill install command
22
+ --dir PATH Directory that contains .cursor/mcp.json (default: cwd)
23
+ --api-url GitLab API URL (default ${DEFAULT_API_URL})
24
+
25
+ Dry-run is the default. --write is required to create or merge .cursor/mcp.json.
26
+ `;
27
+ }
28
+ export function readSetupOptions(flags, directory = process.cwd()) {
29
+ const cursor = flags.cursor === "true";
30
+ const claude = flags.claude === "true";
31
+ if (cursor === claude) {
32
+ throw new CliUsageError("setup requires exactly one of --cursor or --claude");
33
+ }
34
+ return {
35
+ client: cursor ? "cursor" : "claude",
36
+ oauth: flags.oauth === "true" || flags.use_oauth === "true",
37
+ write: flags.write === "true",
38
+ skill: flags.skill === "true",
39
+ directory: flags.dir ?? directory,
40
+ apiUrl: flags.api_url ?? DEFAULT_API_URL,
41
+ };
42
+ }
43
+ export function cursorMcpConfig(options) {
44
+ const env = {
45
+ GITLAB_API_URL: options.apiUrl,
46
+ };
47
+ if (options.oauth) {
48
+ env.GITLAB_USE_OAUTH = "true";
49
+ }
50
+ else {
51
+ env.GITLAB_PERSONAL_ACCESS_TOKEN = "${env:GITLAB_PERSONAL_ACCESS_TOKEN}";
52
+ }
53
+ return {
54
+ mcpServers: {
55
+ gitlab: {
56
+ command: "zereight-mcp-gitlab",
57
+ env,
58
+ },
59
+ },
60
+ };
61
+ }
62
+ export function claudeMcpAddCommand(options) {
63
+ const envFlags = options.oauth
64
+ ? `--env GITLAB_USE_OAUTH=true --env GITLAB_API_URL=${options.apiUrl}`
65
+ : `--env GITLAB_PERSONAL_ACCESS_TOKEN="$GITLAB_PERSONAL_ACCESS_TOKEN" --env GITLAB_API_URL=${options.apiUrl}`;
66
+ return `claude mcp add gitlab --transport stdio --scope local ${envFlags} -- zereight-mcp-gitlab`;
67
+ }
68
+ export function runSetup(options) {
69
+ switch (options.client) {
70
+ case "cursor":
71
+ return runCursorSetup(options);
72
+ case "claude":
73
+ return runClaudeSetup(options);
74
+ default: {
75
+ const exhaustive = options.client;
76
+ throw new Error(`Unhandled setup client: ${String(exhaustive)}`);
77
+ }
78
+ }
79
+ }
80
+ function runCursorSetup(options) {
81
+ const snippet = cursorMcpConfig(options);
82
+ const target = join(options.directory, ".cursor", "mcp.json");
83
+ const body = `${JSON.stringify(snippet, null, 2)}\n`;
84
+ const skillLine = options.skill ? `\nSkill: ${SKILL_INSTALL}\n` : "";
85
+ if (!options.write) {
86
+ return {
87
+ text: `Dry-run Cursor config (pass --write to merge ${target}):\n${body}` + skillLine,
88
+ };
89
+ }
90
+ const merged = mergeMcpJson(readJsonFile(target), snippet);
91
+ mkdirSync(dirname(target), { recursive: true });
92
+ writeFileSync(target, `${JSON.stringify(merged, null, 2)}\n`);
93
+ return {
94
+ text: `Wrote ${target}\n${skillLine}`,
95
+ writtenPath: target,
96
+ };
97
+ }
98
+ function runClaudeSetup(options) {
99
+ const command = claudeMcpAddCommand(options);
100
+ const skillLine = options.skill ? `\nSkill: ${SKILL_INSTALL}\n` : "";
101
+ return {
102
+ text: `${command}\n${skillLine}`,
103
+ };
104
+ }
105
+ function readJsonFile(path) {
106
+ if (!existsSync(path)) {
107
+ return {};
108
+ }
109
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
110
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
111
+ throw new CliUsageError(`${path} is not a JSON object`);
112
+ }
113
+ const copy = {};
114
+ for (const [key, value] of Object.entries(parsed)) {
115
+ copy[key] = value;
116
+ }
117
+ return copy;
118
+ }
119
+ function mergeMcpJson(existing, snippet) {
120
+ const existingServers = typeof existing.mcpServers === "object" &&
121
+ existing.mcpServers !== null &&
122
+ !Array.isArray(existing.mcpServers)
123
+ ? existing.mcpServers
124
+ : {};
125
+ const snippetServers = typeof snippet.mcpServers === "object" &&
126
+ snippet.mcpServers !== null &&
127
+ !Array.isArray(snippet.mcpServers)
128
+ ? snippet.mcpServers
129
+ : {};
130
+ return {
131
+ ...existing,
132
+ mcpServers: {
133
+ ...existingServers,
134
+ ...snippetServers,
135
+ },
136
+ };
137
+ }
package/build/index.js CHANGED
@@ -77,6 +77,8 @@ import { createGitLabOAuthClient, ensureOAuthToken } from "./oauth.js";
77
77
  import { getPositionalCliCommand } from "./cli-command.js";
78
78
  import { runAuthCommandAsync } from "./auth-cli.js";
79
79
  import { isCliInvocation, resolveCli } from "./cli/router.js";
80
+ import { runDoctorWithOptionalProbe, packageVersion, } from "./cli/doctor.js";
81
+ import { runSetup } from "./cli/setup.js";
80
82
  import { formatCliError, formatToolOutput } from "./cli/output.js";
81
83
  import { compactMcpToolResult, readOwnRecord } from "./cli/compact-mcp-result.js";
82
84
  import { compileDeniedToolsRegex } from "./tools/denied-regex.js";
@@ -13005,6 +13007,43 @@ async function main() {
13005
13007
  process.stderr.write(`${cli.message}\n`);
13006
13008
  process.exit(2);
13007
13009
  }
13010
+ if (cli.kind === "setup") {
13011
+ try {
13012
+ const result = runSetup({
13013
+ ...cli.options,
13014
+ apiUrl: getConfig("api-url", "GITLAB_API_URL", cli.options.apiUrl),
13015
+ });
13016
+ process.stdout.write(result.text);
13017
+ process.exit(0);
13018
+ }
13019
+ catch (error) {
13020
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
13021
+ process.exit(2);
13022
+ }
13023
+ }
13024
+ if (cli.kind === "doctor") {
13025
+ try {
13026
+ const report = await runDoctorWithOptionalProbe({
13027
+ credentials: {
13028
+ hasPersonalAccessToken: Boolean(GITLAB_PERSONAL_ACCESS_TOKEN),
13029
+ hasJobToken: Boolean(GITLAB_JOB_TOKEN),
13030
+ hasCookie: Boolean(GITLAB_AUTH_COOKIE_PATH),
13031
+ useOAuth: USE_OAUTH,
13032
+ },
13033
+ apiUrl: GITLAB_API_URL,
13034
+ permissionMode: GITLAB_PERMISSION_MODE,
13035
+ toolsetsRaw: GITLAB_TOOLSETS_RAW,
13036
+ version: packageVersion(),
13037
+ probe: cli.probe ? probeGitLabUser : undefined,
13038
+ });
13039
+ process.stdout.write(report.text);
13040
+ process.exit(report.ok ? 0 : 1);
13041
+ }
13042
+ catch (error) {
13043
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
13044
+ process.exit(1);
13045
+ }
13046
+ }
13008
13047
  if (cli.kind === "auth" || getPositionalCliCommand(process.argv) === "auth") {
13009
13048
  try {
13010
13049
  await runAuthCommandAsync();
@@ -13053,6 +13092,32 @@ async function main() {
13053
13092
  function hasCliCredentials() {
13054
13093
  return Boolean(GITLAB_PERSONAL_ACCESS_TOKEN || GITLAB_JOB_TOKEN || GITLAB_AUTH_COOKIE_PATH || USE_OAUTH);
13055
13094
  }
13095
+ async function probeGitLabUser(apiUrl) {
13096
+ const headers = { Accept: "application/json" };
13097
+ if (GITLAB_PERSONAL_ACCESS_TOKEN) {
13098
+ headers["PRIVATE-TOKEN"] = GITLAB_PERSONAL_ACCESS_TOKEN;
13099
+ }
13100
+ else if (GITLAB_JOB_TOKEN) {
13101
+ headers["JOB-TOKEN"] = GITLAB_JOB_TOKEN;
13102
+ }
13103
+ try {
13104
+ const response = await fetch(`${apiUrl.replace(/\/$/, "")}/user`, { headers });
13105
+ if (response.ok) {
13106
+ return { ok: true, status: response.status, detail: `GET /user ${response.status}` };
13107
+ }
13108
+ return {
13109
+ ok: false,
13110
+ status: response.status,
13111
+ detail: `GET /user ${response.status} ${response.statusText}`,
13112
+ };
13113
+ }
13114
+ catch (error) {
13115
+ return {
13116
+ ok: false,
13117
+ detail: error instanceof Error ? error.message : String(error),
13118
+ };
13119
+ }
13120
+ }
13056
13121
  function isRecord(value) {
13057
13122
  return typeof value === "object" && value !== null && !Array.isArray(value);
13058
13123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.3.8-next.144",
3
+ "version": "2.3.8-next.145",
4
4
  "mcpName": "io.github.zereight/gitlab-mcp",
5
5
  "description": "GitLab MCP server for projects, merge requests, issues, pipelines, wiki, releases, and more",
6
6
  "keywords": [