Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9da7d83
test(docs): cover component release tags
polaz Nov 1, 2026
c786a55
fix: align namespace capabilities and setup docs
polaz Nov 2, 2026
8ba2f7c
test: type unknown capability fixtures
polaz Nov 2, 2026
7ea6d65
fix: encode namespace metadata identifiers
polaz Nov 2, 2026
487f279
docs: correct client configuration examples
polaz Nov 2, 2026
bf15e9f
docs(roo): keep tokens outside project files
polaz Nov 2, 2026
c2680d9
docs: link current GitLab.com rate limits
polaz Nov 2, 2026
253fda8
docs(codex): list archive check prerequisites
polaz Nov 2, 2026
0e3beb6
fix(roo): honor the Linux config directory
polaz Nov 2, 2026
967e1aa
fix(discovery): contain optional metadata errors
polaz Nov 2, 2026
b533496
test: use typed connection feature fixtures
polaz Nov 2, 2026
d80cd96
test: isolate Linux publishing contracts
polaz Nov 2, 2026
25f1dd1
test(roo): compare native path separators
polaz Nov 2, 2026
df2a2f7
ci(docs): quote the release output path
polaz Nov 2, 2026
53bfea7
test: cover namespace credential boundaries
polaz Nov 2, 2026
d83d0a9
fix(docs): refresh after asset publication
polaz Nov 2, 2026
098328c
docs: clarify full-suite prerequisites
polaz Nov 2, 2026
54cc584
docs(codex): explain Yarn preparation
polaz Nov 2, 2026
a916b94
fix(roo): honor VS Code application data
polaz Nov 2, 2026
b7f3325
refactor: validate namespace REST metadata
polaz Nov 2, 2026
c29096c
test: cover unknown namespace capabilities
polaz Nov 2, 2026
9f11a2c
fix(release): require matching MCPB assets
polaz Nov 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ jobs:
run: yarn lint
working-directory: packages/gitlab-mcp

- name: Verify documentation release downloads
run: node --test tests/scripts/docs-download.test.mjs
working-directory: packages/gitlab-mcp

- name: Run tests with coverage
run: yarn test:cov
working-directory: packages/gitlab-mcp
Expand Down
15 changes: 5 additions & 10 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ on:
- 'packages/gitlab-mcp/package.json'
- 'yarn.lock'
- '.github/workflows/docs.yml'
workflow_run:
workflows: ["CI/CD Pipeline"]
types: [completed]
branches: [main]
repository_dispatch:
types: [mcpb-published]
workflow_dispatch:

permissions:
Expand All @@ -26,9 +24,6 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
if: >
github.event_name != 'workflow_run' ||
github.event.workflow_run.conclusion == 'success'
# docs/, vitepress, and the docs scripts live in the core package; run the
# docs steps from there. (uses: steps below set explicit paths where needed.)
defaults:
Expand Down Expand Up @@ -58,8 +53,8 @@ jobs:
mkdir -p docs/public/downloads
LATEST_TAG=$(gh release view --json tagName --jq '.tagName' 2>/dev/null || echo "")
if [ -n "$LATEST_TAG" ]; then
VERSION="${LATEST_TAG#v}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
VERSION="${LATEST_TAG#gitlab-mcp-v}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
gh release download "$LATEST_TAG" --pattern "*.mcpb" --dir docs/public/downloads/ || true
MCPB_FILE="docs/public/downloads/gitlab-mcp-${VERSION}.mcpb"
if [ -f "$MCPB_FILE" ]; then
Expand All @@ -68,7 +63,7 @@ jobs:
else
# Fallback to package.json version if no release exists
VERSION=$(jq -r '.version' package.json)
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "MCPB bundle version from package.json: ${VERSION}"
fi
env:
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,20 @@ jobs:
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ needs.release-please.outputs.release-tag }}
fail_on_unmatched_files: true
files: |
packages/gitlab-mcp/gitlab-mcp-[0-9]*.mcpb
packages/gitlab-mcp/gitlab-mcp-db-*.mcpb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# repository_dispatch can trigger downstream workflows using GITHUB_TOKEN.
# https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow
- name: Refresh documentation downloads
run: gh api --method POST "repos/${GITHUB_REPOSITORY}/dispatches" -f event_type=mcpb-published
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload Codex marketplace to GitHub Release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3
with:
Expand Down
70 changes: 39 additions & 31 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The GitLab MCP integration tests follow a **dependency chain pattern** to ensure
## 🚨 Critical Rules

1. **Tests CANNOT run standalone** - They depend on lifecycle data
2. **Must use `--runInBand`** - Tests must run serially to maintain dependencies
2. **Integration suites run serially** - Use the maintained integration scripts to preserve lifecycle order
3. **Single infrastructure creation** - Only data-lifecycle.test.ts creates data
4. **No individual cleanup** - Only final cleanup in data-lifecycle.test.ts
5. **Real data only** - No mocks, all tests use actual GitLab entities
Expand Down Expand Up @@ -55,25 +55,47 @@ enables `--experimental-vm-modules` so Jest can load jsdom's ESM dependencies fr
the CommonJS settings-panel tests. Use `test`, `test:cov`, `test:integration` or
`test:all` rather than invoking Jest without that runtime option.

### Complete Integration Test Suite (Recommended)
### Select the required suite

Run these from `packages/gitlab-mcp`:

| Command | Executes | External prerequisites |
| --- | --- | --- |
| `yarn test` | Unit tests only | None |
| `yarn test:cov` | Unit tests with coverage | None |
| `yarn test:integration` | Integration suites in lifecycle order | Protected `.env.test`, designated test namespace |
| `yarn test:all` | Unit and integration suites | The same integration environment |

The Linux publishing-workflow regression runs separately from the portable unit
suite and requires Bash. The required Quality Checks job executes it with:

```bash
node --test tests/scripts/docs-download.test.mjs
```

### Complete Unit and Integration Test Suite (requires `.env.test`)
```bash
yarn test
yarn test:all
```

Treat this as full-suite evidence only with the protected `.env.test` configuration
and designated test namespace available. Without that file, the test configuration
selects unit tests only, even when this command succeeds.

This command:
- ✅ Uses all feature flags (WORKITEMS, MILESTONE, PIPELINE, GITLAB_WIKI)
- ✅ Runs tests in dependency order with `--runInBand`
- ✅ Runs suites serially with one worker and lifecycle ordering; accumulated worker memory can be reclaimed between files
- ✅ Creates complete infrastructure once
- ✅ Tests all schemas with real data
- ✅ Cleans up everything at the end

### Individual Test Files (NOT RECOMMENDED)
```bash
# ❌ DON'T DO THIS - Will fail due to missing dependencies
yarn test tests/integration/schemas-dependent/merge-requests.test.ts
yarn test:integration --runTestsByPath tests/integration/schemas-dependent/merge-requests.test.ts

# ✅ If you must, run the full chain:
yarn test
yarn test:integration
```

## 📊 Data Lifecycle Stages
Expand Down Expand Up @@ -163,7 +185,7 @@ describe('My Schema Tests', () => {
```
Error: Test data not available. Make sure to run data-lifecycle.test.ts first with --runInBand
```
**Solution:** Use `yarn test` instead of individual test files.
**Solution:** Use `yarn test:integration` instead of individual dependent files.

### Tests running in parallel
```
Expand All @@ -186,30 +208,16 @@ Test expects MRs but none found
5. **Clean**: Single cleanup operation at the end
6. **Maintainable**: Clear separation between data creation and testing

## ✅ Current Status (2025-01-15)

**🎉 ALL CRITICAL ISSUES RESOLVED**:
- ✅ **Complete test suite passing** - 27/27 test suites, 369/372 tests passing (99.2%)
- ✅ **Unit tests completely rewritten** - Proper mock infrastructure using enhancedFetch
- ✅ **Integration tests fully working** - Real GitLab API testing with data lifecycle
- ✅ **Node-fetch migration complete** - All tests now use native fetch API
- ✅ **Test dependency chain FIXED** - Persistent file storage enables data sharing between test files
- ✅ **Soft-fail patterns eliminated** - All tests use real data or fail properly
- ✅ **Jest configuration enhanced** - Proper serial execution with `--runInBand`

**Test Results Summary**:
- **Total Test Suites**: 27 passed, 0 failed
- **Total Tests**: 369 passed, 3 skipped, 0 failed (372 total)
- **Integration Tests**: 16 suites passing (200+ tests)
- **Unit Tests**: 11 suites passing (169+ tests)
- **Coverage**: 54.92% statements, 37.39% branches, 53.28% functions

**Key Test Categories**:
- ✅ **Data Lifecycle**: 12/12 tests passing - Complete infrastructure setup/teardown
- ✅ **Schema Validation**: 150+ tests passing - All GitLab API schemas validated
- ✅ **Unit Tests**: 169+ tests passing - Full mock-based handler testing
- ✅ **Work Items GraphQL**: 20+ tests passing - Full CRUD with real GitLab instance
- ✅ **Integration API**: 30+ tests passing - Real GitLab API validation
## Current results and installed-client qualification

Use the CI results and coverage report for the exact commit under review. Historical
test counts do not establish the state of a current release, and unit-only results
do not establish that integration suites ran.

The [Codex release qualification procedure](packages/gitlab-mcp/docs/guides/codex-qualification.md)
separates archive/CLI checks, read-only provider checks and manual desktop acceptance.
The [workflow evaluation guide](packages/gitlab-mcp/docs/guides/codex-workflows.md)
describes authenticated client evaluations against deterministic loopback fixtures.

## 🎯 Adding New Tests

Expand Down
1 change: 1 addition & 0 deletions packages/gitlab-mcp/docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ export default defineConfig({
items: [
{ text: "Overview", link: "/guides/" },
{ text: "Codex and Claude Skills", link: "/guides/codex-workflows" },
{ text: "Codex Release Qualification", link: "/guides/codex-qualification" },
{ text: "Duo Sessions", link: "/guides/duo-sessions" },
{ text: "Complete Code Review", link: "/guides/complete-code-review" },
{ text: "Setup CI Notifications", link: "/guides/setup-ci-notifications" },
Expand Down
52 changes: 23 additions & 29 deletions packages/gitlab-mcp/docs/advanced/federation.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Technical deep dive into how GitLab MCP manages multiple GitLab instances with p

## System Architecture

Namespace-aware capability checks are available from 13.2.0. GitLab.com uses
root-namespace subscriptions; self-managed instances retain their instance
license. See [Tier Detection](/advanced/tier-detection) for visibility and unknown
subscription behavior.

```
┌─────────────────────────────────────────────────────────────┐
│ MCP Server (single process) │
Expand All @@ -25,7 +30,7 @@ Technical deep dive into how GitLab MCP manages multiple GitLab instances with p
│ │ v17.2 SaaS │ v16.8 Self │ v16.0 Self │ │
│ │ Rate: 0/100 │ Rate: 5/50 │ Rate: 2/20 │ │
│ └─────────────┴─────────────┴─────────────┘ │
│ NOTE: No tier here! Tier is per-namespace, not instance │
│ SaaS tier: per root namespace; self-managed: per instance │
├─────────────────────────────────────────────────────────────┤
│ SessionManager (per-user sessions) │
│ ┌──────────────────┐ ┌──────────────────┐ │
Expand Down Expand Up @@ -64,12 +69,12 @@ Technical deep dive into how GitLab MCP manages multiple GitLab instances with p
│ │ • User info (username, id) ││
│ └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│ NAMESPACE-LEVEL CACHE (per session) — TTL: 5 min │
│ Tier varies per group/project, especially on gitlab.com │
│ NAMESPACE CACHE (session, instance, credential): TTL 5 min │
│ GitLab.com projects inherit their root subscription │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ "gitlab-org" → Ultimate, features: {epics: ✓, iterations: ✓}││
│ │ "my-free-group" → Free, features: {epics: ✗, iterations: ✗} ││
│ │ "company/proj" → Premium, features: {epics: ✓, ...} ││
│ │ "enterprise" → Ultimate ││
│ │ "personal" → Free ││
│ │ "company" → Unknown when plan metadata is unavailable ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```
Expand Down Expand Up @@ -167,31 +172,20 @@ Request flow:

### NamespaceTierDetector

Detects and caches namespace tier via GraphQL:

```graphql
query GetNamespaceTier($fullPath: ID!) {
namespace(fullPath: $fullPath) {
id
fullPath

... on Group {
plan
}
Observes the REST root namespace's optional `plan` attribute. Numeric project and
group IDs are resolved to paths before looking up their root. Credentials and
instances have separate cache entries, with a five-minute TTL and a total
capacity of 1,024 observations. Missing or unrecognized metadata remains unknown.

... on Project {
group {
plan
}
}
}
}
```
Discovery uses the union of the selected roots. Execution uses the actual target
arguments. Both run inside a request-local tier context; `ConnectionManager`
projects that fact into the existing tool and widget capability checks without
mutating its stored instance information. Unknown tiers defer license decisions
to GitLab while preserving version, token and operator restrictions.

Tier normalization:
- `ultimate`, `gold` → Ultimate
- `premium`, `silver`, `bronze`, `starter` → Premium
- Everything else → Free
See [Tier Detection](/advanced/tier-detection) for the REST contract, normalization
and bounded lookup behavior. The cache is disposable and independent on each
replica; it is not an authorization or session store.

### TokenContext

Expand Down
Loading
Loading