@simplysf/simply-gitlab-mcp 0.4.0 → 0.5.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 +7 -0
- package/docs/guides/mcp-server.md +425 -0
- package/oclif.manifest.json +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -100,6 +100,13 @@ they reach you, but the text itself is data, never instructions.
|
|
|
100
100
|
|
|
101
101
|
**Your token never appears in output.** It is redacted from every error message and response body.
|
|
102
102
|
|
|
103
|
+
## Guides
|
|
104
|
+
|
|
105
|
+
Setup, client configuration, and the tool catalogue:
|
|
106
|
+
**https://simplysf.github.io/simply-cli/gitlab/guides/mcp-server/**
|
|
107
|
+
|
|
108
|
+
That guide also ships inside this package, at [`docs/guides/mcp-server.md`](docs/guides/mcp-server.md).
|
|
109
|
+
|
|
103
110
|
## Documentation
|
|
104
111
|
|
|
105
112
|
- [Migration and architecture](../../docs/design/0002-migration-from-gitlab-mcp.md)
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: MCP server
|
|
3
|
+
description: Give an AI agent in Claude Desktop, Claude Code, Cursor, Gemini CLI, or VS Code the same GitLab capabilities as the CLI, as Model Context Protocol tools, read-only by default.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`@simplysf/simply-gitlab-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
7
|
+
server with one tool per CLI command. It calls the same library the CLI is built on, in-process, so
|
|
8
|
+
the agent gets the same credential handling, `dryRun` previews, read-only guard, and error messages
|
|
9
|
+
described elsewhere in these guides, and the same raw JSON the CLI prints with `--json`.
|
|
10
|
+
|
|
11
|
+
Using it takes four steps: install the server, tell your MCP client how to launch it, check the
|
|
12
|
+
connection, then ask the agent for what you want in plain language. This page walks through each,
|
|
13
|
+
then covers letting the agent write, the full tool list, results and errors, and what to check when
|
|
14
|
+
something does not work.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
The server needs Node.js 22 or later.
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install -g @simplysf/simply-gitlab-mcp
|
|
22
|
+
simply-gitlab-mcp --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`--help` prints the options and every tool the server can register, so it doubles as a check that
|
|
26
|
+
the install worked. It prints to stderr, because stdout is reserved for the protocol stream.
|
|
27
|
+
|
|
28
|
+
You can skip the global install and have the client fetch the package on demand with
|
|
29
|
+
`npx -y @simplysf/simply-gitlab-mcp` as the command; the client configurations below show both
|
|
30
|
+
forms.
|
|
31
|
+
|
|
32
|
+
## Connection settings
|
|
33
|
+
|
|
34
|
+
The server reads the same variables as the CLI, described in [Credentials](/guides/credentials/):
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
GITLAB_TOKEN=glpat-...
|
|
38
|
+
GITLAB_URL=https://gitlab.example.com # omit entirely for gitlab.com
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
There are only two, and `GITLAB_URL` defaults to `https://gitlab.com` — a gitlab.com user needs the
|
|
42
|
+
token and nothing else.
|
|
43
|
+
|
|
44
|
+
Put them in a `.env` file and pass it with `--env-file`, or set them in the `env` block of the
|
|
45
|
+
client's server entry. A variable already in the environment wins over the file, as it does for the
|
|
46
|
+
CLI. Settings are resolved when a tool is called rather than at startup, so a missing token is
|
|
47
|
+
reported by the tool that needed it instead of killing a server the client has already launched.
|
|
48
|
+
|
|
49
|
+
Give the token `read_api` scope unless you intend to enable writes. That is the only layer that
|
|
50
|
+
actually binds — see [Write safety](/guides/write-safety/).
|
|
51
|
+
|
|
52
|
+
Use an absolute path for the env file. MCP clients launch the server from a working directory of
|
|
53
|
+
their own, so `~` and relative paths may not resolve. On Windows, write the path with forward
|
|
54
|
+
slashes or doubled backslashes inside JSON: `"C:/Users/me/gitlab.env"`.
|
|
55
|
+
|
|
56
|
+
## Configure a client
|
|
57
|
+
|
|
58
|
+
### Claude Desktop
|
|
59
|
+
|
|
60
|
+
Edit `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/`; Windows:
|
|
61
|
+
`%APPDATA%\Claude\`) and restart Claude Desktop:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"simply-gitlab": {
|
|
67
|
+
"command": "simply",
|
|
68
|
+
"args": ["gitlab", "mcp", "--env-file", "/home/me/gitlab.env"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The same entry with the settings inline and no global install:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"simply-gitlab": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["gitlab", "mcp"],
|
|
82
|
+
"env": {
|
|
83
|
+
"GITLAB_URL": "https://gitlab.example.com",
|
|
84
|
+
"GITLAB_TOKEN": "glpat-..."
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Claude Code
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
claude mcp add simply-gitlab -- simply-gitlab-mcp --env-file ~/gitlab.env
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Add `--scope project` to write the entry to a `.mcp.json` at the repository root instead, which can
|
|
98
|
+
be committed so a team shares the server definition. Keep tokens out of a committed file by
|
|
99
|
+
pointing at an env file each person holds locally. Inside a session, `/mcp` lists the configured
|
|
100
|
+
servers and whether each one connected.
|
|
101
|
+
|
|
102
|
+
### Cursor
|
|
103
|
+
|
|
104
|
+
Cursor reads `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every project, with the
|
|
105
|
+
same `mcpServers` shape as Claude Desktop.
|
|
106
|
+
|
|
107
|
+
### Gemini CLI
|
|
108
|
+
|
|
109
|
+
Gemini CLI reads `~/.gemini/settings.json` for every project, or `.gemini/settings.json` inside a
|
|
110
|
+
project, with the same `mcpServers` shape as Claude Desktop:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"simply-gitlab": {
|
|
116
|
+
"command": "simply",
|
|
117
|
+
"args": ["gitlab", "mcp", "--env-file", "/home/me/gitlab.env"]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`gemini mcp add` writes the same entry. `--scope user` puts it in the global file; the default
|
|
124
|
+
`project` scope writes `.gemini/settings.json`, and `-e` sets one variable on the entry:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
gemini mcp add --scope user \
|
|
128
|
+
-e GITLAB_URL=https://gitlab.example.com \
|
|
129
|
+
-e GITLAB_TOKEN=glpat-... \
|
|
130
|
+
simply-gitlab simply-gitlab-mcp
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Flags meant for the server itself, `--env-file` and `--allow-writes`, are easier to add by editing
|
|
134
|
+
`settings.json`, because `gemini mcp add` reads a leading-dash argument as one of its own options.
|
|
135
|
+
|
|
136
|
+
Values in `env` expand `$NAME` from the environment Gemini started in, which keeps the token out of
|
|
137
|
+
the settings file without an env file:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"simply-gitlab": {
|
|
143
|
+
"command": "npx",
|
|
144
|
+
"args": ["gitlab", "mcp"],
|
|
145
|
+
"env": {
|
|
146
|
+
"GITLAB_URL": "https://gitlab.example.com",
|
|
147
|
+
"GITLAB_TOKEN": "$GITLAB_TOKEN"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Two other fields on an entry matter here. `trust: true` bypasses the per-call confirmation for every
|
|
155
|
+
tool on that server, so leave it off on any entry started with `--allow-writes` and let a person
|
|
156
|
+
approve each write. `includeTools` and `excludeTools` narrow what the model is shown, which is a
|
|
157
|
+
second way to express the split in [Let the agent write](#let-the-agent-write): a write entry can
|
|
158
|
+
register the merge-request tools while holding back the ones that push commits.
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"mcpServers": {
|
|
163
|
+
"simply-gitlab-write": {
|
|
164
|
+
"command": "simply",
|
|
165
|
+
"args": ["gitlab", "mcp", "--allow-writes", "--env-file", "/home/me/gitlab-write.env"],
|
|
166
|
+
"excludeTools": ["gitlab_commit_create", "gitlab_file_create", "gitlab_file_update"],
|
|
167
|
+
"trust": false
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Inside a session, `/mcp` lists the configured servers, whether each connected, and the tools it
|
|
174
|
+
registered.
|
|
175
|
+
|
|
176
|
+
### Gemini Code Assist
|
|
177
|
+
|
|
178
|
+
Agent mode in the VS Code extension reads that same `~/.gemini/settings.json`, so an entry added for
|
|
179
|
+
Gemini CLI is already in place. In IntelliJ the file is `mcp.json` in the IDE's configuration
|
|
180
|
+
directory, holding the same `mcpServers` object.
|
|
181
|
+
|
|
182
|
+
### VS Code
|
|
183
|
+
|
|
184
|
+
VS Code reads `.vscode/mcp.json`, with a `servers` key and an explicit transport type:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"servers": {
|
|
189
|
+
"simply-gitlab": {
|
|
190
|
+
"type": "stdio",
|
|
191
|
+
"command": "simply",
|
|
192
|
+
"args": ["gitlab", "mcp", "--env-file", "/home/me/gitlab.env"]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Any other client
|
|
199
|
+
|
|
200
|
+
The server speaks MCP over stdio, so any client that can launch a local command works: give it the
|
|
201
|
+
command, its arguments, and optionally an environment, as above. It announces itself as
|
|
202
|
+
`simply-gitlab` with the package version during the handshake and supplies instructions that
|
|
203
|
+
summarise the tool conventions for the host's model.
|
|
204
|
+
|
|
205
|
+
## Check the connection
|
|
206
|
+
|
|
207
|
+
Ask the agent to list the projects you are a member of. It should call `gitlab_project_list` with
|
|
208
|
+
`membership: true` and come back with paths and ids. If it does, the server is running and the
|
|
209
|
+
token works; the [troubleshooting](#troubleshooting) table covers the cases where it does not.
|
|
210
|
+
|
|
211
|
+
There is no `whoami` tool, because GitLab has no cheap identity endpoint worth a tool of its own —
|
|
212
|
+
a project listing answers the same question and gives the agent the paths it needs next.
|
|
213
|
+
|
|
214
|
+
## Ask for what you want
|
|
215
|
+
|
|
216
|
+
You do not call tools yourself. Describe the outcome, and the agent chooses the tools and fills in
|
|
217
|
+
their inputs from the descriptions the server publishes. Some examples of what a request turns
|
|
218
|
+
into:
|
|
219
|
+
|
|
220
|
+
| You ask | The agent calls |
|
|
221
|
+
| ----------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
222
|
+
| "Why did CI fail on group/project?" | `gitlab_ci_job_list` with `scope: ["failed"]`, then `gitlab_ci_job_log` on the first |
|
|
223
|
+
| "What merge requests am I reviewing?" | `gitlab_mr_list` with `reviewerUsername` |
|
|
224
|
+
| "Summarise MR 42 and what changed in it." | `gitlab_mr_view`, then `gitlab_commit_list` on the source branch |
|
|
225
|
+
| "Where is `UMCN_Account` defined?" | `gitlab_search` with `scope: "blobs"` and a `project` |
|
|
226
|
+
| "When did `src/index.ts` last change, and who?" | `gitlab_commit_list` with `path: "src/index.ts"` |
|
|
227
|
+
| "Show me the README on the `release` branch." | `gitlab_file_view` with `ref: "release"` |
|
|
228
|
+
| "What's deployed to production?" | `gitlab_ci_environment_list` |
|
|
229
|
+
| "Which CI variables are set for production?" | `gitlab_ci_variable_list` — keys and scopes only, never values |
|
|
230
|
+
|
|
231
|
+
Each tool's inputs are the matching command's flags in camel case, with arrays where the CLI takes
|
|
232
|
+
a repeatable or comma-separated flag; see the [Command Reference](/reference/) for what each does.
|
|
233
|
+
The second request above, for instance, becomes a call like:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{ "project": "group/project", "state": "opened", "reviewerUsername": "me", "limit": 20 }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
and returns the same envelope the CLI prints under `--json`:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{ "items": [...], "total": 3, "pages": 1, "complete": true }
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
A few things make the agent's job easier:
|
|
246
|
+
|
|
247
|
+
- **Name the project by path.** `group/subgroup/project` works everywhere a numeric id does, and
|
|
248
|
+
the agent does not have to look the id up first. It is not URL-encoded by the caller.
|
|
249
|
+
- **Merge requests go by `iid`.** That is the number in the web URL, and it is what the tools take
|
|
250
|
+
— not the `id` that also appears in the payload. "MR 42" is unambiguous; the tools validate the
|
|
251
|
+
shape and refuse anything that is not a positive whole number.
|
|
252
|
+
- **`complete: false` means there is more.** Every list returns `{ items, total?, pages, complete }`,
|
|
253
|
+
and a `false` there means the limit cut the results short. Ask for a higher `limit` rather than
|
|
254
|
+
assuming the list is the whole answer.
|
|
255
|
+
- **Job logs come back tailed.** `gitlab_ci_job_log` returns the last 200 lines by default, because
|
|
256
|
+
a trace is regularly tens of megabytes. `tail: 0` returns all of it, which is rarely what you
|
|
257
|
+
want in a context window.
|
|
258
|
+
- **Blob search needs Advanced Search.** The `blobs`, `commits`, `wiki_blobs`, `notes`, and
|
|
259
|
+
`snippet_titles` scopes need Elasticsearch enabled on the instance. Where it is not, GitLab
|
|
260
|
+
answers with an empty list and a `200` rather than an error — so an empty blob search may mean
|
|
261
|
+
"not enabled" rather than "no matches". The tool description says so, so the agent can tell you
|
|
262
|
+
that instead of concluding the code does not exist.
|
|
263
|
+
|
|
264
|
+
## Let the agent write
|
|
265
|
+
|
|
266
|
+
Started without options, the server registers only the 16 read tools. Start it with
|
|
267
|
+
`--allow-writes` to also register the six that create a branch, write a file, push a commit, or
|
|
268
|
+
open and update a merge request. Even then, `GITLAB_READ_ONLY` in the environment refuses every
|
|
269
|
+
write, exactly as it does for the CLI.
|
|
270
|
+
|
|
271
|
+
That mirrors the two-credential-file arrangement in [Write safety](/guides/write-safety/): give the
|
|
272
|
+
agent's everyday server a `read_api` token, and configure a second server entry with
|
|
273
|
+
`--allow-writes` and an `api` token that is enabled only when a person means to let the agent
|
|
274
|
+
write. In an `mcpServers` configuration the pair looks like this:
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"mcpServers": {
|
|
279
|
+
"simply-gitlab": {
|
|
280
|
+
"command": "simply",
|
|
281
|
+
"args": ["gitlab", "mcp", "--env-file", "/home/me/gitlab.env"]
|
|
282
|
+
},
|
|
283
|
+
"simply-gitlab-write": {
|
|
284
|
+
"command": "simply",
|
|
285
|
+
"args": ["gitlab", "mcp", "--allow-writes", "--env-file", "/home/me/gitlab-write.env"]
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
A `read_api` token is still the only layer that binds; the server-side default just keeps write
|
|
292
|
+
tools out of the agent's normal loop entirely. Most clients also ask you to approve each tool call,
|
|
293
|
+
and the tools carry MCP annotations (`readOnlyHint`, `destructiveHint`) so a host can tell a read
|
|
294
|
+
from a write when it asks.
|
|
295
|
+
|
|
296
|
+
With writes allowed:
|
|
297
|
+
|
|
298
|
+
- **Ask for a preview first.** All six write tools accept `dryRun: true`, which returns the request
|
|
299
|
+
that would be sent without sending it. "Show me what you'd send to open a merge request from
|
|
300
|
+
`feature/thing` into `main`, then do it" produces a call like the one below, which you can check
|
|
301
|
+
before the agent repeats it without `dryRun`.
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"project": "group/project",
|
|
306
|
+
"sourceBranch": "feature/thing",
|
|
307
|
+
"targetBranch": "main",
|
|
308
|
+
"title": "feat: the thing",
|
|
309
|
+
"dryRun": true
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
- **Nothing here deletes.** There is no delete tool in the catalogue, and so no `confirm` gate: every
|
|
314
|
+
write either creates something or edits a merge request, and both are recoverable. That is why
|
|
315
|
+
`dryRun` carries the weight it does.
|
|
316
|
+
- **A file update replaces the whole file.** There is no partial edit, so an agent must read the
|
|
317
|
+
file before writing it. Passing `lastCommitId` from `gitlab_file_view` makes GitLab reject the
|
|
318
|
+
write if anything changed meanwhile, instead of silently overwriting someone.
|
|
319
|
+
- **A batch commit is atomic.** `gitlab_commit_create` takes an array of actions — create, update,
|
|
320
|
+
delete, move, chmod — and writes them as one commit. Each action is validated before anything is
|
|
321
|
+
sent, so a malformed entry comes back naming its own index rather than as an opaque `400`.
|
|
322
|
+
|
|
323
|
+
## Tools
|
|
324
|
+
|
|
325
|
+
| Read tools (always) | Write tools (`--allow-writes`) |
|
|
326
|
+
| ---------------------------- | ------------------------------ |
|
|
327
|
+
| `gitlab_project_list` | `gitlab_file_create` |
|
|
328
|
+
| `gitlab_project_view` | `gitlab_file_update` |
|
|
329
|
+
| `gitlab_file_view` | `gitlab_branch_create` |
|
|
330
|
+
| `gitlab_branch_list` | `gitlab_commit_create` |
|
|
331
|
+
| `gitlab_commit_list` | `gitlab_mr_create` |
|
|
332
|
+
| `gitlab_commit_view` | `gitlab_mr_update` |
|
|
333
|
+
| `gitlab_commit_diff` | |
|
|
334
|
+
| `gitlab_mr_list` | |
|
|
335
|
+
| `gitlab_mr_view` | |
|
|
336
|
+
| `gitlab_tag_list` | |
|
|
337
|
+
| `gitlab_release_list` | |
|
|
338
|
+
| `gitlab_ci_job_list` | |
|
|
339
|
+
| `gitlab_ci_job_log` | |
|
|
340
|
+
| `gitlab_ci_variable_list` | |
|
|
341
|
+
| `gitlab_ci_environment_list` | |
|
|
342
|
+
| `gitlab_search` | |
|
|
343
|
+
|
|
344
|
+
### CI variable values are never returned
|
|
345
|
+
|
|
346
|
+
`gitlab_ci_variable_list` reports each variable's key, environment scope, and whether it is
|
|
347
|
+
protected or masked. Every **value** comes back as `<hidden>`, and there is no input to reveal them.
|
|
348
|
+
|
|
349
|
+
GitLab's own API does not work this way: `GET /projects/:id/variables` returns every value in clear
|
|
350
|
+
text, including the ones marked "masked", because masking only hides a value in job logs. Those
|
|
351
|
+
values are deploy keys and production credentials, and a value revealed to an agent lands in a
|
|
352
|
+
context window, a transcript, and whatever that agent writes next.
|
|
353
|
+
|
|
354
|
+
This is the one place the MCP server deliberately offers less than the CLI. A person who needs a
|
|
355
|
+
value runs `simply gitlab ci variable list --reveal`.
|
|
356
|
+
|
|
357
|
+
## Results and errors
|
|
358
|
+
|
|
359
|
+
A successful call returns what the CLI prints with `--json`, verbatim. A failure returns an error
|
|
360
|
+
result whose text is a JSON object with a stable `code`:
|
|
361
|
+
|
|
362
|
+
| `code` | Meaning |
|
|
363
|
+
| -------- | ------------------------------------------------------------------------------------ |
|
|
364
|
+
| `config` | Missing or contradictory settings, a refused write, bad input. |
|
|
365
|
+
| `auth` | The instance rejected the credentials; carries the HTTP `status`. |
|
|
366
|
+
| `error` | Any other failure, including an API error with its HTTP `status` and sanitised body. |
|
|
367
|
+
|
|
368
|
+
These map one-to-one onto the CLI's exit codes 2, 3, and 1, described in
|
|
369
|
+
[Scripts and agents](/guides/scripting/), and carry the same `name` and `message`, scrubbed of
|
|
370
|
+
credentials and control characters the same way. The agent sees these errors and can act on them,
|
|
371
|
+
so a missing token or a wrong project path usually ends in the agent telling you what went wrong
|
|
372
|
+
rather than in silence.
|
|
373
|
+
|
|
374
|
+
Text the instance produced — merge request descriptions, commit messages, file contents, job traces
|
|
375
|
+
— is written by anyone who can push or open a merge request. Control characters are stripped before
|
|
376
|
+
it reaches the agent, and the tool descriptions say plainly that the content is data rather than
|
|
377
|
+
instructions.
|
|
378
|
+
|
|
379
|
+
## Troubleshooting
|
|
380
|
+
|
|
381
|
+
Run `simply-gitlab-mcp --help` at a terminal first. If it prints the tool list, the package is
|
|
382
|
+
installed and working, and the problem is in how the client launches it or what it passes.
|
|
383
|
+
|
|
384
|
+
| Symptom | Cause and fix |
|
|
385
|
+
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
386
|
+
| The client reports the server failed to start | Desktop apps do not load your shell profile, so the command may not be on their `PATH`. Use the full path to the executable (it lives in the directory `npm prefix -g` reports, under `bin/` on macOS and Linux), or the `npx` form above. |
|
|
387
|
+
| The client's log says `Env file ... not found.` | The only startup failure. The path is wrong or relative; make it absolute. The server exits with code 2. |
|
|
388
|
+
| Every tool returns `code: "config"` naming `GITLAB_TOKEN` | The settings are not reaching the server: the env file is not being read, or the variable was set in a shell the client did not inherit. Pass `--env-file` or an `env` block explicitly. |
|
|
389
|
+
| `code: "auth"` with status `401` | The token is wrong, expired, or revoked. Create a new one under **Preferences › Access tokens**. |
|
|
390
|
+
| `code: "auth"` with status `403` on a write | The token is `read_api`-scoped, or your role on the project does not permit the change. The first is the intended outcome for the everyday server; use the write-capable entry for this change. |
|
|
391
|
+
| `code: "error"` with status `404` on a project you can see | The project reference is wrong. Use the numeric id, or the full path including every parent group — and do not URL-encode it yourself. |
|
|
392
|
+
| A merge request tool acts on the wrong merge request | An `id` was passed where an `iid` belongs. The `iid` is the number in the web URL; both are integers, so nothing rejects the wrong one. |
|
|
393
|
+
| The agent says it has no tool to create or update anything | The server was started without `--allow-writes`. That is the default; see [Let the agent write](#let-the-agent-write). |
|
|
394
|
+
| A write returns `code: "config"` mentioning `GITLAB_READ_ONLY` | The variable is set in the environment or the env file. Unset it, or point the write server at a credential file meant for writing. |
|
|
395
|
+
| A blob search returns nothing for code you know exists | Advanced Search is probably not enabled on the instance; GitLab answers those scopes with an empty list and a `200`. Try `scope: "merge_requests"` or the project's file tree instead. |
|
|
396
|
+
|
|
397
|
+
## Embed in your own process
|
|
398
|
+
|
|
399
|
+
The stdio binary is the usual way to run the server, but the package also exports what it is built
|
|
400
|
+
from, so a host process can serve the same tools over a transport of its choosing:
|
|
401
|
+
|
|
402
|
+
```js
|
|
403
|
+
import { createServer, startServer } from '@simplysf/simply-gitlab-mcp';
|
|
404
|
+
|
|
405
|
+
// Serve over stdio, exactly as the binary does.
|
|
406
|
+
await startServer({ allowWrites: false, envFile: '/home/me/gitlab.env' });
|
|
407
|
+
|
|
408
|
+
// Or build the server and attach a transport yourself.
|
|
409
|
+
const server = createServer({ allowWrites: true, env: process.env });
|
|
410
|
+
await server.connect(transport);
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`createServer` returns the MCP SDK's `McpServer` with the tools registered and nothing connected.
|
|
414
|
+
`TOOLS` is the full catalogue, `selectTools(allowWrites)` is the subset a given configuration
|
|
415
|
+
registers, and `invokeTool` and `mapError` run one tool and map a failure onto the error object
|
|
416
|
+
above, for tests and hosts that need to drive the server without a transport.
|
|
417
|
+
|
|
418
|
+
## Options
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
simply-gitlab-mcp [--allow-writes] [--env-file <path>]
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
`--help` prints the options and the full tool list. Because stdout is the protocol stream, it prints
|
|
425
|
+
to stderr.
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysf/simply-gitlab-mcp",
|
|
3
3
|
"description": "Model Context Protocol server exposing GitLab to AI agents, built on @simplysf/simply-gitlab-core",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"author": "@ClayChipps",
|
|
6
6
|
"homepage": "https://github.com/SimplySF/simply-gitlab",
|
|
7
7
|
"bugs": "https://github.com/SimplySF/simply-gitlab/issues",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"node": ">=22.0.0"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
+
"/docs",
|
|
17
18
|
"/lib",
|
|
18
19
|
"/oclif.manifest.json"
|
|
19
20
|
],
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
37
38
|
"@oclif/core": "^4.11.2",
|
|
38
|
-
"@simplysf/simply-gitlab-core": "^0.
|
|
39
|
+
"@simplysf/simply-gitlab-core": "^0.4.0",
|
|
39
40
|
"zod": "^4.5.4"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
@@ -183,5 +184,5 @@
|
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
},
|
|
186
|
-
"gitHead": "
|
|
187
|
+
"gitHead": "1695d8c8c195df25f1bda573e1f3367ad1d31ded"
|
|
187
188
|
}
|