diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 664f045c2..2883cc69b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -414,7 +414,11 @@ jobs: # Only the committed dynamic artifacts. .semantic-release-version is a # release-time-only file and must not be committed. The root # package.json (../../) carries the mirrored release version. - git add package.json README.md server.json ../../package.json + # prepare-release.sh renders the README to all three shipping locations + # (core npm page, GitHub repo landing, db npm page) -- stage every one so + # the public counts/version never drift between them. Missing the root and + # db READMEs here previously left them stuck a release behind the core page. + git add package.json README.md server.json ../../README.md ../../package.json ../gitlab-mcp-db/README.md if git diff --cached --quiet; then echo "No metadata drift; nothing to commit." else diff --git a/README.md b/README.md index b0f553969..c75b3d2ba 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Advanced GitLab MCP server — 58 CQRS tools exposing 230 GitLab operations across 26 entity types. The tool catalog and parameters are filtered to each instance's GitLab version, tier, and token scopes, so the agent sees only what the connected instance actually supports. -[![Install in Claude Desktop](https://img.shields.io/badge/Claude_Desktop-Install_Extension-F97316?style=for-the-badge)](https://gitlab-mcp.sw.foundation/downloads/gitlab-mcp-9.0.0.mcpb) +[![Install in Claude Desktop](https://img.shields.io/badge/Claude_Desktop-Install_Extension-F97316?style=for-the-badge)](https://gitlab-mcp.sw.foundation/downloads/gitlab-mcp-9.1.0.mcpb) [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP_Server-007ACC?style=for-the-badge&logo=visualstudiocode&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22gitlab-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40structured-world%2Fgitlab-mcp%22%5D%7D) [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_MCP_Server-24bfa5?style=for-the-badge&logo=visualstudiocode&logoColor=white)](vscode-insiders:mcp/install?%7B%22name%22%3A%22gitlab-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40structured-world%2Fgitlab-mcp%22%5D%7D) diff --git a/package.json b/package.json index 6b8016750..e67954dd8 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "@hono/node-server": "1.19.14", "diff": "4.0.4", "esbuild": ">=0.28.1", - "tmp": ">=0.2.7" + "tmp": ">=0.2.7", + "form-data": "^4.0.6" }, "scripts": { "build": "nx run-many -t build", diff --git a/packages/gitlab-mcp-db/README.md b/packages/gitlab-mcp-db/README.md index cc63ec6f4..e03f9a8a4 100644 --- a/packages/gitlab-mcp-db/README.md +++ b/packages/gitlab-mcp-db/README.md @@ -4,7 +4,7 @@ Advanced GitLab MCP server — 58 CQRS tools exposing 230 GitLab operations across 26 entity types. The tool catalog and parameters are filtered to each instance's GitLab version, tier, and token scopes, so the agent sees only what the connected instance actually supports. -[![Install in Claude Desktop](https://img.shields.io/badge/Claude_Desktop-Install_Extension-F97316?style=for-the-badge)](https://gitlab-mcp.sw.foundation/downloads/gitlab-mcp-9.0.0.mcpb) +[![Install in Claude Desktop](https://img.shields.io/badge/Claude_Desktop-Install_Extension-F97316?style=for-the-badge)](https://gitlab-mcp.sw.foundation/downloads/gitlab-mcp-9.1.0.mcpb) [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_MCP_Server-007ACC?style=for-the-badge&logo=visualstudiocode&logoColor=white)](vscode:mcp/install?%7B%22name%22%3A%22gitlab-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40structured-world%2Fgitlab-mcp%22%5D%7D) [![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install_MCP_Server-24bfa5?style=for-the-badge&logo=visualstudiocode&logoColor=white)](vscode-insiders:mcp/install?%7B%22name%22%3A%22gitlab-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40structured-world%2Fgitlab-mcp%22%5D%7D) diff --git a/packages/gitlab-mcp-db/package.json b/packages/gitlab-mcp-db/package.json index 2d0c100f7..beb6bf3b3 100644 --- a/packages/gitlab-mcp-db/package.json +++ b/packages/gitlab-mcp-db/package.json @@ -28,7 +28,7 @@ "devDependencies": { "@structured-world/gitlab-mcp": "workspace:^", "@types/jest": "^30.0.0", - "@types/node": "^25.9.3", + "@types/node": "^25.9.4", "jest": "^30.4.2", "ts-jest": "^29.4.11", "typescript": "^6.0.3" diff --git a/packages/gitlab-mcp/package.json b/packages/gitlab-mcp/package.json index 13e02dd25..f7a24510e 100644 --- a/packages/gitlab-mcp/package.json +++ b/packages/gitlab-mcp/package.json @@ -558,11 +558,11 @@ "build:mcpb": "./scripts/build-mcpb.sh" }, "dependencies": { - "@clack/prompts": "^1.5.1", + "@clack/prompts": "^1.6.0", "@modelcontextprotocol/sdk": "^1.29.0", "express": "^5.2.1", - "graphql": "^16.14.2", - "graphql-tag": "^2.12.6", + "graphql": "^17.0.0", + "graphql-tag": "^2.12.7", "open": "^11.0.0", "picomatch": "^4.0.4", "pino": "^10.3.1", @@ -574,13 +574,13 @@ "zod": "^4.4.3" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20260616.1", + "@cloudflare/workers-types": "^4.20260621.1", "@eslint/js": "^10.0.1", "@graphql-typed-document-node/core": "^3.2.0", "@structured-world/vue-privacy": "^1.10.0", "@types/express": "^5.0.6", "@types/jest": "^30.0.0", - "@types/node": "^25.9.3", + "@types/node": "^25.9.4", "@types/picomatch": "^4.0.3", "@typescript-eslint/eslint-plugin": "^8.61.1", "@typescript-eslint/parser": "^8.61.1", diff --git a/packages/gitlab-mcp/scripts/prepare-release.sh b/packages/gitlab-mcp/scripts/prepare-release.sh index e0ea52ecb..9e847e626 100755 --- a/packages/gitlab-mcp/scripts/prepare-release.sh +++ b/packages/gitlab-mcp/scripts/prepare-release.sh @@ -48,11 +48,15 @@ fi echo "prepare-release: v${VERSION}, ${TOOL_COUNT} tools (${READONLY_TOOL_COUNT} read-only, ${ACTION_COUNT} actions), ${ENTITY_COUNT} entities" -# Update server.json: version + description +# Update server.json: version + description. +# The description is the public marketing line shown on the MCP Registry listing, +# so it mirrors the README hero sentence (tools / operations / entity types) instead +# of a generic blurb. The MCP Registry schema caps description at 100 chars; this +# phrasing stays well under that even with 3-digit counts. # Note: set -e ensures script exits on jq failure; leftover server.tmp is benign # (gitignored, cleaned by next successful run, doesn't affect release) -jq --arg v "$VERSION" --arg tc "$TOOL_COUNT" \ - '.version = $v | .packages[0].version = $v | .description = "GitLab MCP server with " + $tc + " tools for projects, MRs, pipelines, and more"' \ +jq --arg v "$VERSION" --arg tc "$TOOL_COUNT" --arg ac "$ACTION_COUNT" --arg ec "$ENTITY_COUNT" \ + '.version = $v | .packages[0].version = $v | .description = $tc + " CQRS tools exposing " + $ac + " GitLab operations across " + $ec + " entity types"' \ server.json > server.tmp && mv server.tmp server.json # Generate README.md from the template for each location it ships to. __REPO_BASE__ diff --git a/packages/gitlab-mcp/server.json b/packages/gitlab-mcp/server.json index c9dc86fa4..43e3e9760 100644 --- a/packages/gitlab-mcp/server.json +++ b/packages/gitlab-mcp/server.json @@ -3,7 +3,7 @@ "$comment": "Schema version 2025-12-11 is the current MCP Registry standard. Only essential env vars are exposed here; USE_* feature flags are documented at websiteUrl and available via MCPB toggles.", "name": "io.github.structured-world/gitlab-mcp", "title": "Advanced GitLab MCP server", - "description": "GitLab MCP server with 58 tools for projects, MRs, pipelines, and more", + "description": "58 CQRS tools exposing 230 GitLab operations across 26 entity types", "websiteUrl": "https://gitlab-mcp.sw.foundation", "repository": { "url": "https://github.com/structured-world/gitlab-mcp", diff --git a/packages/gitlab-mcp/src/entities/runners/schema-readonly.ts b/packages/gitlab-mcp/src/entities/runners/schema-readonly.ts index e55e1c15a..6be6dc3d4 100644 --- a/packages/gitlab-mcp/src/entities/runners/schema-readonly.ts +++ b/packages/gitlab-mcp/src/entities/runners/schema-readonly.ts @@ -93,22 +93,24 @@ const ListRunnerJobsSchema = z.object({ action: z.literal('list_jobs').describe('List jobs that have run on a runner'), runner_id: runnerIdField, statuses: z - .enum([ - 'CREATED', - 'PENDING', - 'RUNNING', - 'FAILED', - 'SUCCESS', - 'CANCELED', - 'SKIPPED', - 'MANUAL', - 'SCHEDULED', - 'WAITING_FOR_RESOURCE', - 'PREPARING', - 'CANCELING', - ]) + .array( + z.enum([ + 'CREATED', + 'PENDING', + 'RUNNING', + 'FAILED', + 'SUCCESS', + 'CANCELED', + 'SKIPPED', + 'MANUAL', + 'SCHEDULED', + 'WAITING_FOR_RESOURCE', + 'PREPARING', + 'CANCELING', + ]), + ) .optional() - .describe('Filter jobs by status'), + .describe('Filter jobs by one or more statuses (e.g. ["FAILED", "CANCELED"])'), first: firstField, after: afterField, }); diff --git a/packages/gitlab-mcp/src/graphql/runners.ts b/packages/gitlab-mcp/src/graphql/runners.ts index 7138f57d6..25b98cadf 100644 --- a/packages/gitlab-mcp/src/graphql/runners.ts +++ b/packages/gitlab-mcp/src/graphql/runners.ts @@ -186,12 +186,12 @@ export interface ListRunnerJobsResult { } export interface ListRunnerJobsVars { id: string; - statuses?: string | null; + statuses?: string[] | null; first?: number | null; after?: string | null; } export const LIST_RUNNER_JOBS: TypedDocumentNode = gql` - query ListRunnerJobs($id: CiRunnerID!, $statuses: CiJobStatus, $first: Int, $after: String) { + query ListRunnerJobs($id: CiRunnerID!, $statuses: [CiJobStatus!], $first: Int, $after: String) { runner(id: $id) { id jobs(statuses: $statuses, first: $first, after: $after) { diff --git a/packages/gitlab-mcp/tests/unit/entities/runners/registry.test.ts b/packages/gitlab-mcp/tests/unit/entities/runners/registry.test.ts index 858eac112..13fae06d1 100644 --- a/packages/gitlab-mcp/tests/unit/entities/runners/registry.test.ts +++ b/packages/gitlab-mcp/tests/unit/entities/runners/registry.test.ts @@ -112,10 +112,18 @@ describe('runners registry', () => { it('list_jobs queries the runner jobs connection', async () => { mockClient.request.mockResolvedValueOnce({ runner: { id: RUNNER_GID, jobs: { nodes: [] } } }); - await browse().handler({ action: 'list_jobs', runner_id: 7, statuses: 'FAILED' }); + await browse().handler({ + action: 'list_jobs', + runner_id: 7, + statuses: ['FAILED', 'CANCELED'], + }); const [doc, vars] = mockClient.request.mock.calls[0]; expect(doc).toBe(LIST_RUNNER_JOBS); - expect(vars).toMatchObject({ id: RUNNER_GID, statuses: 'FAILED' }); + // GitLab's jobs(statuses:) argument is [CiJobStatus!]; the schema takes a + // list so callers can filter by several statuses at once. Declaring the + // variable as a bare enum made GitLab reject the query with + // "List dimension mismatch on variable $statuses". + expect(vars).toMatchObject({ id: RUNNER_GID, statuses: ['FAILED', 'CANCELED'] }); }); it('list_jobs throws when the runner is missing', async () => { diff --git a/yarn.lock b/yarn.lock index 0ca53a294..386b5bcc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -450,13 +450,13 @@ __metadata: languageName: node linkType: hard -"@clack/core@npm:1.4.1": - version: 1.4.1 - resolution: "@clack/core@npm:1.4.1" +"@clack/core@npm:1.4.2": + version: 1.4.2 + resolution: "@clack/core@npm:1.4.2" dependencies: fast-wrap-ansi: "npm:^0.2.0" sisteransi: "npm:^1.0.5" - checksum: 10c0/7e7e02f0c423c457aff9acc09f9ffbae3d2841b18f543c614b5b8eaa2c11c9f146bf68b3078d1daa4dea9d3131ccfa514edbe8d29ced18446933567188b2543c + checksum: 10c0/390a0c650dbd4c406029e32fd6a0b704bad3a172cab96e77989a1d376ddeee5f4b0500edb093375b202ae35acfc94fbd2fb11fe3ad9e47724acae4b7ff1d68b6 languageName: node linkType: hard @@ -471,22 +471,22 @@ __metadata: languageName: node linkType: hard -"@clack/prompts@npm:^1.5.1": - version: 1.5.1 - resolution: "@clack/prompts@npm:1.5.1" +"@clack/prompts@npm:^1.6.0": + version: 1.6.0 + resolution: "@clack/prompts@npm:1.6.0" dependencies: - "@clack/core": "npm:1.4.1" + "@clack/core": "npm:1.4.2" fast-string-width: "npm:^3.0.2" fast-wrap-ansi: "npm:^0.2.0" sisteransi: "npm:^1.0.5" - checksum: 10c0/423b1958d28a25703130c2dd6a236413d210446fd36e980dcf106f9165e5c579494214aaca5185269e918488c6d4f93c4de44a7dc5f902add8874486b2624c22 + checksum: 10c0/b288a1085ce75a06a739f4907b38187d9b3121a53e4a86541383df3e2348276fa00903a5d2220d7b2c821a55b81ad11af9cadb3760e3212dac27fe2001555ab1 languageName: node linkType: hard -"@cloudflare/workers-types@npm:^4.20260616.1": - version: 4.20260616.1 - resolution: "@cloudflare/workers-types@npm:4.20260616.1" - checksum: 10c0/ce02ec49826740e35de0c22c45b4fe18e819a890c26c17261f81a1de5f7b7e59824182bff4775ce50b6614d5cd468d1b1393b76956467404ba62f69aaa3305d8 +"@cloudflare/workers-types@npm:^4.20260621.1": + version: 4.20260621.1 + resolution: "@cloudflare/workers-types@npm:4.20260621.1" + checksum: 10c0/5f220aa5feff5b4e20dc549b82f80740c0daa25cb001e8d8a5aac51ab2aba1123096276ec880887c6fd37cc396beb43ebe4ab3c23ae32d31993fa2230c1e4ed7 languageName: node linkType: hard @@ -2136,7 +2136,7 @@ __metadata: "@prisma/client": "npm:^7.8.0" "@structured-world/gitlab-mcp": "workspace:^" "@types/jest": "npm:^30.0.0" - "@types/node": "npm:^25.9.3" + "@types/node": "npm:^25.9.4" jest: "npm:^30.4.2" prisma: "npm:^7.8.0" ts-jest: "npm:^29.4.11" @@ -2148,15 +2148,15 @@ __metadata: version: 0.0.0-use.local resolution: "@structured-world/gitlab-mcp@workspace:packages/gitlab-mcp" dependencies: - "@clack/prompts": "npm:^1.5.1" - "@cloudflare/workers-types": "npm:^4.20260616.1" + "@clack/prompts": "npm:^1.6.0" + "@cloudflare/workers-types": "npm:^4.20260621.1" "@eslint/js": "npm:^10.0.1" "@graphql-typed-document-node/core": "npm:^3.2.0" "@modelcontextprotocol/sdk": "npm:^1.29.0" "@structured-world/vue-privacy": "npm:^1.10.0" "@types/express": "npm:^5.0.6" "@types/jest": "npm:^30.0.0" - "@types/node": "npm:^25.9.3" + "@types/node": "npm:^25.9.4" "@types/picomatch": "npm:^4.0.3" "@typescript-eslint/eslint-plugin": "npm:^8.61.1" "@typescript-eslint/parser": "npm:^8.61.1" @@ -2166,8 +2166,8 @@ __metadata: eslint: "npm:^10.5.0" eslint-plugin-prettier: "npm:^5.5.6" express: "npm:^5.2.1" - graphql: "npm:^16.14.2" - graphql-tag: "npm:^2.12.6" + graphql: "npm:^17.0.0" + graphql-tag: "npm:^2.12.7" jest: "npm:^30.4.2" open: "npm:^11.0.0" picomatch: "npm:^4.0.4" @@ -2460,12 +2460,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^25.9.3": - version: 25.9.3 - resolution: "@types/node@npm:25.9.3" +"@types/node@npm:^25.9.4": + version: 25.9.4 + resolution: "@types/node@npm:25.9.4" dependencies: undici-types: "npm:>=7.24.0 <7.24.7" - checksum: 10c0/72d3aece9d42c2c641bcd3f3cb2dc2828b4bd384dfcbd910c404b8859a68bd69d50c4769ce7defd4ff5e049768e23e615f09407ea2cbbb5f44b90d75a7c6b8ca + checksum: 10c0/4b19670ef5dbefa836a2d4a9ed0b5ac6befbc0844bfdd12833234ff8fa68da1f65e6362d2ef87acf7e7da9d6ff649bf81c454d12e802b7e7fa9246a15c0edcf6 languageName: node linkType: hard @@ -4953,16 +4953,16 @@ __metadata: languageName: node linkType: hard -"form-data@npm:4.0.5, form-data@npm:^4.0.5": - version: 4.0.5 - resolution: "form-data@npm:4.0.5" +"form-data@npm:^4.0.6": + version: 4.0.6 + resolution: "form-data@npm:4.0.6" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" es-set-tostringtag: "npm:^2.1.0" - hasown: "npm:^2.0.2" - mime-types: "npm:^2.1.12" - checksum: 10c0/dd6b767ee0bbd6d84039db12a0fa5a2028160ffbfaba1800695713b46ae974a5f6e08b3356c3195137f8530dcd9dfcb5d5ae1eeff53d0db1e5aad863b619ce3b + hasown: "npm:^2.0.4" + mime-types: "npm:^2.1.35" + checksum: 10c0/43947a77bf0ff45c6ceed789778982d47a3f3e720a74b71721174ebf3310a5f1a8be1d6b38a3ee3688e8a18a2c4273073ec0844cd37efda3eaf46d41c9c318ff languageName: node linkType: hard @@ -5224,21 +5224,21 @@ __metadata: languageName: node linkType: hard -"graphql-tag@npm:^2.12.6": - version: 2.12.6 - resolution: "graphql-tag@npm:2.12.6" +"graphql-tag@npm:^2.12.7": + version: 2.12.7 + resolution: "graphql-tag@npm:2.12.7" dependencies: tslib: "npm:^2.1.0" peerDependencies: - graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/7763a72011bda454ed8ff1a0d82325f43ca6478e4ce4ab8b7910c4c651dd00db553132171c04d80af5d5aebf1ef6a8a9fd53ccfa33b90ddc00aa3d4be6114419 + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 10c0/c5b973b6b0477d3f734a0df7ebea2061790e268774d7fea310fde64820c1686c3b43f3e7262e4224d689917f42ae8e84c67f32772c003c308d38896ed960c5ec languageName: node linkType: hard -"graphql@npm:^16.14.2": - version: 16.14.2 - resolution: "graphql@npm:16.14.2" - checksum: 10c0/a95a96961eaff55cc9fe9d31fae6f33499ac988b972d07ea5085024cb1333f515b902f376e7393a5489aa82200a8aff3eb96580e4d1b69d702ed19b6eb1ce97a +"graphql@npm:^17.0.0": + version: 17.0.1 + resolution: "graphql@npm:17.0.1" + checksum: 10c0/95e4766eefa97bbc37cb4452527d24e12068f170e03c564611a3911f30a77361dfcfbdbfeea00f643c82585c02312c1f41ba6a6263049996d279c49951454980 languageName: node linkType: hard @@ -5292,6 +5292,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 + languageName: node + linkType: hard + "hast-util-to-html@npm:^9.0.5": version: 9.0.5 resolution: "hast-util-to-html@npm:9.0.5" @@ -6631,7 +6640,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:2.1.35, mime-types@npm:^2.1.12": +"mime-types@npm:2.1.35, mime-types@npm:^2.1.35": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: