Skip to content

v3.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 31 Oct 01:06
· 75 commits to main since this release
Immutable release. Only release title and notes can be modified.
v3.4.0
39b7093

Summary

3.4.0 serves input schemas the Anthropic Messages API accepts on every surface, can ask a person its questions in Spanish, follows GitLab.com's move of the Orbit routes, and tightens HTTP mode's authentication budgets, its listener and the configuration it starts with.

  • No tool carries allOf, anyOf, oneOf, not, if, then or else at the root of its input schema (#1509). The Anthropic Messages API refuses a whole request whose tool list holds one, and 3.3.0 served one on several individual tools and on every meta tool in the compact and full parameter-schema modes. Each rule stays in the catalog, which gitlab_find_action and gitlab://tools read, and the handler enforces it before anything is sent.
  • GITLAB_MCP_LANGUAGE=es (or --language=es) serves the texts a person reads directly in Spanish: the questions and field titles of the guided flows, the confirmation of a destructive action and the progress messages (#1577 to #1581, ADR-0027). What the model reads stays in English, and a server that sets nothing, or en, serves the same texts as 3.3.0.
  • Orbit (#1585, #1593): GitLab.com removed five of the six REST routes the Orbit tools read on 27 and 28 October, and orbit.status, orbit.schema, orbit.tools, orbit.dsl and orbit.graph_status now read GitLab's GraphQL API and its Orbit MCP endpoint, under the same action IDs, tool names and parameters. Since Orbit's general availability on 29 October, GitLab.com serves a namespace's index and queries only for a top-level group with the orbit_promo add-on or an Orbit trial; orbit.graph_status publishes such a group's Orbit access and trial to a token with the Owner role there, and answers a group without access with them rather than with an error.
  • HTTP mode: the authentication budgets key an IPv6 address by its /64 and bound each /48, and unix in --trusted-proxies lets a proxy on a unix socket name its callers through --trusted-proxy-header (#1481). The two server cards and the RFC 9728 metadata are rate-limited per client address, at 20 requests a second with a burst of 200 by default (--discovery-rate-limit-rps, --discovery-rate-limit-burst, 0 to turn it off) (#1483). A listener off loopback that serves plain HTTP warns at startup, and GITLAB_MCP_ALLOW_PLAINTEXT_HTTP=false (or --allow-plaintext-http=false) refuses one (#1561). A session refused because it is not open for the presented credential is no longer described as belonging to another credential, which was false of one that idled out or did not survive a restart (#1484).
  • Configuration (#1519 to #1524): a zero means off wherever it has a meaning, through the flag and the variable alike, and 0s reads as 0; a value the server cannot use refuses the start on both transports instead of being replaced or ignored (see Breaking changes). A stdio server no longer reads the settings only HTTP mode has, and names any it finds set, and it now honors GITLAB_MCP_SESSION_REVALIDATE_INTERVAL. --max-listen-streams sets the listen ceiling as a flag.
  • gitlab_find_action ranks the action a plural noun names first, so list issues answers issue.list (#1470). A string parameter GitLab checks (an email address, a URL, a commit SHA, a cron expression, a semantic version and others) declares its format or pattern, and find's example call fills it with a value of that form (#1504). Every text block of a result is annotated with the priority the output format reference documents, 0.4 for a list, 0.6 for a read and 0.8 for a write, where 3.3.0 annotated most with 0.7 (#1499).
  • New parameters: client-go v3.30.0 (#1460), with member_role_id on the access levels of project and group protected branches (Ultimate); an avatar on group.create, project.create, project.create_for_user, user.create, user.modify, admin.topic_create and admin.topic_update, remove_avatar on group.update, project.update and admin.topic_update, and 39 more override_params keys on project.import_from_file (#1640); hide_backlog_list and hide_closed_list on group.group_board_update (#1510); archived on project.label_list, and ref on search.commits and search.wiki for a project search (#1635). The board listings name every column with a work-in-progress limit (#1512), mr_review.changes_get publishes its pagination (#1584), and security_scan_profile.attach takes the business_logic (GitLab 19.5) and skillguard (GitLab 19.6) default profiles by name (#1585).
  • Fixes:
    • An inline comment or draft note on a file past the first page of a merge request's diff is no longer refused as outside the diff, and the five prompts that read a merge request's changed files say when they read only the first page (#1584, from #1517 by Nikita Turchaninov).
    • access.approve_project and access.approve_group refuse a level the project or group does not grant, where 0 used to grant Developer and another level left the request pending while it was reported as approved; every membership access level parameter offers only the levels its route accepts (#1567).
    • On the meta and dynamic surfaces, a protected environment approval rule given a count and no grantee was sent as a rule for Maintainers, and an update of a rule by its id moved its grantee to Maintainers; the dynamic surface also turned a top-level required_approval_count into such a rule. Both are now refused before a request on every surface (#1503).
    • merge_request.cancel_auto_merge falls back to the deprecated route when GitLab 19.5 or later answers cancel_auto_merge with 404, as a node can during a rolling upgrade (#1594).
    • A 405 for a project named by a path it no longer has says to retry with the project's ID, instead of reading as a refusal of the merge request, file or token the call was about (#1496).
    • job.trace removes the timestamp a runner writes in front of each line and joins a line the runner wrote in pieces, so tail_lines counts the lines the job printed (#1498).
    • The external status check create and delete name the Maintainer role on the 403 GitLab 19.5 answers a caller without it (#1463), and the safe-mode preview names both spellings that turn safe mode off (#1497).
  • Release and project: the image is built on Alpine 3.24.3 (#1554), and its packages are held to Trivy's vulnerability database before anything is pushed (#1553). Every release binary is rebuilt from its tag and compared byte for byte, binaries from this release on are stamped with the full commit, and scripts/install.sh honors DESTDIR (#1557). Each release opens with a written summary (#1555), and the PyPI wheels carry PEP 740 publish attestations (#1551). The telemetry guide has a worked OpenTelemetry collector example (#1471), the repository states its governance, review standard and roadmap (#1556) and a security policy that describes what happens (#1560), and every commit is signed off under the Developer Certificate of Origin (#1562).

Breaking changes

Configurations that started under 3.3.0 with a value the server ignored or replaced now refuse to start, naming the setting:

  • A unix-socket --http-addr whose --trusted-proxies names addresses and not unix, which used to start with --trusted-proxy-header ignored, so every caller on the socket shared one budget (#1481).
  • --oauth-cache-ttl at 0 or below, in either authentication mode, as GITLAB_MCP_OAUTH_CACHE_TTL already was (#1519).
  • A negative --session-timeout, --revalidate-interval or --pool-idle-timeout (#1520).
  • In HTTP mode, an upload size (--upload-max-file-size, GITLAB_MCP_UPLOAD_MAX_FILE_SIZE) that does not parse, is not positive or lies above 1 TB, as stdio already refused; on both transports, a size whose byte count does not fit in 64 bits, such as 17179869185GB (#1524).
  • With telemetry on, an identity policy the server does not know, or, with no key configured, a key rotation that does not parse or lies outside 0 to 720h (#1523).

HTTP mode's budgets are shared differently (#1481): every address of one IPv6 /64 spends one fast budget and one distinct-credential budget, as the callers behind one IPv4 NAT share an address's, and ten /64s of one /48 that had a credential refused inside --auth-distinct-token-window block the whole /48 for every credential the deployment does not already serve. --auth-failure-window=0 no longer turns the distinct-credential budget off, and GITLAB_MCP_SESSION_TIMEOUT=0 turns the idle timeout off instead of refusing the start (#1519).

The Orbit outputs no longer carry what GitLab.com stopped sending (#1585): the status's user, system, timestamp and formatted_text, and each component's replicas and metrics; the schema's schema_version, each domain's description and each node's incoming and outgoing edges; and the index's formatted_text, last_started_at, last_duration_ms and last_error. response_format on the status and the index is accepted and changes nothing, and orbit.query refuses a query sent as a JSON object, since GitLab.com removed the JSON query DSL on 27 October.

Withdrawn, as the versioning policy allows in a minor release for what a tool could never honor:

  • pagination, page_token and order_by on group.service_account_pat_list and project.service_account_pat_list, whose routes answer every keyset request with 405 and read no order_by; sort takes the eight orders the routes document in place of asc and desc (#1573).
  • new_epic on user.notification_project_update, which the project route does not declare (#1570).
  • repository_checksum_failed and wiki_checksum_failed on project.list, project.list_forks, project.list_user_projects, project.list_user_starred and project.list_user_contributed, which no route reads, so a list asked for them answered every project (#1570).
  • access_level_description on the approval rules of environment.protected_protect, environment.protected_update, group.protected_env_protect and group.protected_env_update, which no route declares (#1503).
  • The name, path, namespace_id, default_branch and lfs_enabled keys of override_params on project.import_from_file, which the import drops (#1640).
  • Below the tier GitLab needs for them, the parameters GitLab drops or refuses there (#1569, #1570):
    • Premium: on group.update, file_template_project_id, ip_restriction_ranges, allowed_email_domains_list, prevent_forking_outside_group, only_allow_merge_if_pipeline_succeeds, allow_merge_on_skipped_pipeline and only_allow_merge_if_all_discussions_are_resolved; wiki_access_level on group.create and group.update; repository_storage on group.list and group.subgroups; approver_ids on merge_request.list, merge_request.list_global and merge_request.list_group; new_epic on user.notification_global_update and user.notification_group_update; on project.create and project.create_for_user, external_authorization_classification_label, use_custom_template, template_project_id, group_with_project_templates_id, mirror and mirror_trigger_builds; and on project.update, external_authorization_classification_label, allow_pipeline_trigger_approve_deployment, ci_restrict_pipeline_cancellation_role, issues_template, merge_requests_template, import_url, mirror, mirror_trigger_builds, mirror_user_id, only_mirror_protected_branches, mirror_overwrites_diverged_branches and mirror_branch_regex.
    • Ultimate: with_security_reports on group.projects; member_role_id on project.member_add, group.group_member_add, group.ldap_link_add and group.saml_link_add; and prevent_merge_without_jira_issue on project.update.

Refused before a request, where GitLab refused the call or did something other than it asked (#1502, #1503, #1567):

  • group.ldap_link_add and group.ldap_link_delete with both or neither of cn and filter.
  • On environment.protected_protect, environment.protected_update, group.protected_env_protect and group.protected_env_update: a protect with no deploy access level, a deploy access level naming other than exactly one of access_level, user_id and group_id, an approval rule naming none of the three unless _destroy is true, an access_level other than 20, 30, 40 or 60, and a top-level required_approval_count above 0, which GitLab refuses as deprecated.
  • access.approve_project and access.approve_group at a level the project or group does not grant, 0 included, which used to grant Developer.

Upgrading

  1. A unix-socket deployment that lists proxies in --trusted-proxies adds unix to the list.
  2. Remove an --oauth-cache-ttl of 0 or below, which ran an oauth deployment at the 15-minute default and was never read in legacy mode, and turn a negative --session-timeout, --revalidate-interval or --pool-idle-timeout into 0, which is what the server made of it.
  3. Correct the upload size the server names at startup: in HTTP mode it ran under 2GB, or under the 1 TB ceiling for a size above it, and on either transport a size whose byte count does not fit in 64 bits ran as a much smaller or an unlimited one.
  4. With telemetry on, correct the identity policy (none, pseudonymous or full) or the key rotation the server names at startup; while it was wrong, the server recorded nothing about callers.
  5. A listener off loopback that serves plain HTTP behind a proxy that terminates TLS sets GITLAB_MCP_ALLOW_PLAINTEXT_HTTP=true (or --allow-plaintext-http) to say so, which logs the line at INFO instead of warning. A container that keeps the image's command sets the variable, since an argument after the image name replaces the command.
  6. Send orbit.query its query as GQL text.
  7. Leave a withdrawn parameter out of a call. An imported project takes its name, path and namespace from the import's own name, path and namespace, and its default branch and LFS setting from a project.update after it; the service account token lists are ordered with one of the eight sort values, such as created_desc, rather than with order_by and asc or desc.
  8. Approve an access request at Developer by leaving access_level out, rather than sending 0.
  9. Name the grantee of every protected environment approval rule (access_level: 40 for Maintainers), which the meta and dynamic surfaces used to fill in, and send a required count as the rule's required_approvals rather than as a top-level required_approval_count.

Security

  • Fixes GHSA-rxjw-584q-hxjr (medium, CVSS 4.7, CWE-23, CWE-20): a tool argument placed into the path of a request to GitLab was not checked for path separators or dot segments, so a crafted value, such as one a model fills in under prompt injection, could make the server build a request path that, once a front layer or GitLab resolves the dot segments, names a different GitLab API endpoint than the action intends, under the configured token and past the server's per-action controls. Every release from 1.0.0 through 3.3.0 is affected: the job artifact, template, model registry download and pipeline schedule variable actions, and any action whose escaped path argument is . or .. as a whole, from 1.0.0, the system hook URL-variable actions from 2.0.2, and the group hook URL-variable, custom-header and test actions from 2.3.0. A standard GitLab install does not serve such a request (400 by default from GitLab 18.2, 404 before it); a front layer that resolves dot segments before forwarding lets it through. 3.4.0 fixes it: the transport refuses to send any request whose path carries a . or .. segment, as given or once percent-decoded; the sixteen actions the advisory names also refuse such an argument before building the request, naming the parameter; and the handlers of the routes that placed an argument into the path unescaped now escape it, so a % in those arguments is sent as %25. The 1.x and 2.x module paths carry no patched release, so upgrading to 3.4.0 is the remedy.
  • Dependencies: Go 1.27.3 (#1455), a security release of crypto/x509, fixes GO-2026-6809 (CVE-2026-111943), which the 3.3.0 binaries carry, and golang.org/x/net v0.62.0 (#1564) fixes GO-2026-6860 and GO-2026-6866 in x/net/html. The image moves to Alpine 3.24.3 (#1554), which fixes CVE-2026-85091 in zlib and CVE-2026-107891 in musl, both in the 3.3.0 image.
  • Hardening without an advisory, in HTTP mode:
    • An IPv6 caller that changed its address inside its /64 was given a fresh fast budget and a fresh distinct-credential budget on every request; the budgets now key the /64 and count the refused /64s of each /48 (#1481).
    • Behind --trusted-proxy-header, the transport-source budget kept a record of every (source, key) pair it charged, both chosen by the caller, until its five-minute sweep, including for sources its limiter never recorded; it now keeps a pair only for a failure its limiter counted, and at most 500 for one source (#1482).

Changelog

✨ Features

  • 05cdb35: feat(http)!: key the authentication budgets on the IPv6 /64, bound the /48 and name the unix-socket peer (#1481) (@jmrplens)
  • d4b958b: feat(http): warn on a plaintext listener off loopback, and refuse it in a hardened mode (#1561) (@jmrplens)
  • 1e4e1e1: feat(tools): offer every param the option struct an action sends already models, and declare the ones GitLab drops (R-PARAM) (#1640) (@jmrplens)

🐛 Bug Fixes

  • d4e6ec0: fix(externalstatuschecks): hint the Maintainer role on the 403 a create or delete gets from 19.5 (#1463) (@jmrplens)
  • 9c467c7: fix(graphql): stop selecting crashState, and hold every document to the schema of GitLab 19.5 (#1647) (@jmrplens)
  • fffe119: fix(groupboards): send the Open and Closed list switches on the group board update (#1510) (@jmrplens)
  • 1fc9bad: fix(mergerequests): retry the deprecated cancel route after a 404 (#1594) (@KashifManzer)
  • a82c704: fix(orbit): read the Orbit status, schema, tools, query language and index from the sources GitLab.com still serves (#1585) (@jmrplens)
  • f486238: fix: answer the review of the best practices stack, update every dependency and regenerate the testing reference (#1564) (@jmrplens)

📚 Documentation

  • 251d071: docs(security): make the policy describe what happens, and publish an assurance case (#1560) (@jmrplens)
  • 8a54699: docs(security): record the October 2026 security review, and check the documentation site's headers after each deploy (#1559) (@jmrplens)
  • 21e93d0: docs(upstream-bugs): keep row 78's merge request to severities and leave row 79 to GitLab's existing issue (#1487) (@jmrplens)
  • 4ac0119: docs(upstream-bugs): name the client-go!3063 commits by their current hashes (#1454) (@jmrplens)
  • f114a89: docs(upstream-bugs): name the merge request that sends row 6 again (#1476) (@jmrplens)
  • 2419d1c: docs(upstream-bugs): record client-go's joint merge request closed and split (#1457) (@jmrplens)
  • c5c42c4: docs(upstream-bugs): record row 53's merge and the rows that moved since 2026-10-19 (#1490) (@jmrplens)
  • ed18d7d: docs(upstream-bugs): record row 55's merge and the rows that moved since 2026-10-22 (#1495) (@jmrplens)
  • 6bc5d1d: docs: move the long-form references out of CLAUDE.md (#1453) (@jmrplens)
  • 6c9066a: docs: say that web commits are signed off now that the repository requires it (#1566) (@jmrplens)
  • e237441: docs: stop describing row 34's joint merge request as open (#1458) (@jmrplens)
  • b6c94ed: docs: write down governance, roles, continuity, code review and a roadmap, and show the OpenSSF Best Practices badge (#1556) (@jmrplens)

🚧 Maintenance

  • fc6486e: Add a gate that refuses a closed httptest server whose address is still used (#1473) (@jmrplens)
  • d52ad65: Add a worked OpenTelemetry collector example, held to the server by the collector module (#1471) (@jmrplens)
  • e846186: Add the person-facing message catalog and the GITLAB_MCP_LANGUAGE setting, English only (#1577) (@jmrplens)
  • 96b4413: Answer the group settings, custom role and group variable rows of issue 1240 from GitLab's source (#1569) (@jmrplens)
  • 1b31f66: Answer the issue, merge request, notification, push mirror and project rows of issue 1240 from GitLab's source (#1570) (@jmrplens)
  • fcf6ea0: Bound the transport-source budget's record of the pairs it charged (#1482) (@jmrplens)
  • 9146b5f: Check inline comment positions past the first page of a merge request's diff, publish the pagination of mr_review.changes_get, and say when a prompt read only the first page of changed files (#1584) (@jmrplens)
  • ffa14bc: Derive every action's content kind in the catalog, so a list, a read and a write carry 0.4, 0.6 and 0.8 (#1499) (@jmrplens)
  • d6f108b: Describe the live record's keyset half and R-PAGE's, cover the token lists end to end, and regenerate the stack's artifacts (#1575) (@jmrplens)
  • 3a5f984: Document the language setting, record ADR-0027, and close #1547 (#1581) (@jmrplens)
  • 5b8d57f: Gate package and exported-symbol doc comments, and fix what the audit reports (#1478) (@jmrplens)
  • f085317: Give each limit's zero one meaning, and write its default once (#1519) (@jmrplens)
  • dbd1b4a: Give tests a dead endpoint that keeps its port, and move the sixteen closed-server sites onto it (#1472) (@jmrplens)
  • d4a08c9: Hold protected environment entries to GitLab's rules before they are sent (#1503) (@jmrplens)
  • 431e25d: Hold the Go commands a credentialed job runs to this module, retire what nothing needed, and declare every script that is not Go (#1550) (@jmrplens)
  • 2a331bf: Leave no mutant lived, uncovered or timed out in the md-escaping and action-ids gates (#1488) (@jmrplens)
  • d450c30: Make the stdio pin of register row 11 tell its two failures apart (#1469) (@jmrplens)
  • 39b7093: Merge commit from fork (@jmrplens)
  • 08263e3: Mirror to GitLab over IPv4, retry the push and leave Dependabot out, like every other repository (#1493) (@jmrplens)
  • 22e6603: Name both safe-mode spellings in the preview hint (#1497) (@jmrplens)
  • ef06f1d: Name each limited board column, its ceilings and its metric in the project and group board listings (#1512) (@jmrplens)
  • 1ee10c3: Offer Ko-fi beside GitHub Sponsors in the sponsor button and the npm funding field (#1489) (@jmrplens)
  • 1376e58: Offer no keyset pagination or order_by on the service account token lists, and publish the eight sort orders they document (#1573) (@jmrplens)
  • cd0861e: Offer only the access levels each GitLab route accepts, and stop reporting a pending access request as approved (#1567) (@jmrplens)
  • b95d8bb: Probe what GitLab 19.5 counts against an invalid token, and state the distinct-credential budget's reason from it (#1480) (@jmrplens)
  • de493dc: Publish the form GitLab checks on a string and fill find's example with it (#1504) (@jmrplens)
  • b2bf38b: Rank the action a plural noun names first in gitlab_find_action (#1470) (@jmrplens)
  • 491eb0a: Rate-limit the discovery documents per client address (#1483) (@jmrplens)
  • 0fd65b6: Read a timestamped job log as GitLab's viewer does: no header, and a line written in pieces whole again (#1498) (@jmrplens)
  • e5d4a81: Read group boards through client-go and the captured answer instead of requests of their own (#1511) (@jmrplens)
  • 4d3b622: Read no HTTP-only setting on stdio, honor the revalidation interval there, and refuse negative duration flags (#1520) (@jmrplens)
  • ae420eb: Read the answers to the edition tier audit's parameter findings from a declaration table, and answer the seven Minimal access rows (#1568) (@jmrplens)
  • 04937dc: Read the twelve settings issue 957 found around the configuration package through it, and give the listen ceiling its flag (#1522) (@jmrplens)
  • 9f1eae6: Record the malformed values issue 957 keeps, and hold every GITLAB_MCP_ read outside the configuration rule (#1521) (@jmrplens)
  • 779d7cb: Record the zeros issue 958 decided, the capacity rule and why each fixed value is fixed (#1518) (@jmrplens)
  • 3be10e4: Record which routes can page by keyset at schema 5, and hold page_token to them in R-PAGE (#1574) (@jmrplens)
  • 2d35ffc: Refuse a malformed or oversized upload size in HTTP mode as stdio does, and state it on RQB-007 (#1524) (@jmrplens)
  • f72d514: Refuse a start whose telemetry identity setting cannot be used, and say what a malformed value of each setting does (#1523) (@jmrplens)
  • b6a6eb0: Refuse an ended session in words true of it (#1484) (@jmrplens)
  • a3fb213: Require exactly one of cn or filter on the LDAP group link add and delete (#1502) (@jmrplens)
  • a066752: Run OpenSSF Scorecard on main, hold a container action's image to its digest, and group the README badges (#1552) (@jmrplens)
  • e420540: Say what a 405 for a moved project's old path means, so the caller retries with the ID (#1496) (@jmrplens)
  • 7a50406: Scan the image's Alpine packages with Trivy before the push, and refuse Aqua's Trivy actions in a credentialed job (#1553) (@jmrplens)
  • 59a2143: Serve no combinator at the root of a tool's input schema, which the Anthropic Messages API refuses (#1509) (@jmrplens)
  • 9199b1a: Serve the person-facing texts in Spanish with GITLAB_MCP_LANGUAGE=es (#1580) (@jmrplens)
  • 4e6a848: Serve the texts a person reads from the message catalog, with English unchanged (#1579) (@jmrplens)
  • a0e4e6c: Share the scaffolding the source gates had copied, in goprogram and a new sourcegate package (#1479) (@jmrplens)
  • 430de90: Skip the Orbit live tests only on GitLab's own answer that Orbit is not served, hold the live tier test to the fixture group's plan, and say that a shortest path names its relationship types (#1593) (@jmrplens)
  • b8846b9: Take Alpine 3.24.3 for zlib and musl, and expressive-code 0.44.3 for postcss-selector-parser (#1554) (@jmrplens)
  • f1ab52b: Teach the Markdown escaping audit the message catalog and add the person-facing text gate (#1578) (@jmrplens)
  • ff18ee5: The GitLab release sync waits for the mirror push (#1494) (@jmrplens)
  • c077123: Wire the closed-server gate into make analyze and CI, and document netfixture (#1474) (@jmrplens)
  • 1debc22: audit(1to1): adjudicate the routes no action reaches and gate on them (#1642) (@jmrplens)
  • 703f46f: audit(1to1): compare nested objects both ways and pair the converters toolutil declares (#1643) (@jmrplens)
  • 031ba7e: audit(1to1): give every workaround waiting on client-go a predicate the pinned SDK is held to (#1636) (@jmrplens)
  • 567ec29: audit(1to1): hold an id-or-path identifier to an input that accepts both (#1638) (@jmrplens)
  • 44fd2b9: audit(1to1): hold every live route to an action or a reason (R-ROUTE) (#1641) (@jmrplens)
  • adb1afd: audit(1to1): hold every option struct a call sends to an input, and a copy between two to every field they share (#1635) (@jmrplens)
  • 00a2cee: audit(1to1): report a captured key the client-go struct beside it already models (#1637) (@jmrplens)
  • ca54e00: audit(1to1): report every param a reached route declares that nothing here sends (R-PARAM) (#1639) (@jmrplens)
  • 1974696: chore(deps): bump client-go to v3.30.0 and take the custom role on protected branch access levels (#1460) (@jmrplens)
  • b9c5512: chore(deps): dependency sweep of 20 October (#1455) (@jmrplens)
  • 0e6334b: chore(release): move VERSION to 3.4.0, with its release notes (#1648) (@jmrplens)
  • 0b20816: chore: carry a copyright line and SPDX-License-Identifier: MIT in every source file (#1563) (@jmrplens)
  • 2f8021c: chore: index R-PARAM and R-ROUTE beside the other audit dimensions and regenerate the artifacts at the top of the auditor stack (#1644) (@jmrplens)
  • 56758fb: chore: regenerate the artifacts at the top of the access level and edition tier stack (#1571) (@jmrplens)
  • 958c651: chore: regenerate the artifacts at the top of the localization stack (#1582) (@jmrplens)
  • 59e16d7: chore: regenerate the artifacts the exactly-one, list-item and format schemas move (#1505) (@jmrplens)
  • 8e959c3: chore: regenerate the artifacts the group board layers move (#1513) (@jmrplens)
  • e9389a0: chore: regenerate the artifacts the job trace usage line and the stack's tests move (#1500) (@jmrplens)
  • fb60a22: chore: regenerate the artifacts the status check usage line moves (#1466) (@jmrplens)
  • 6fbaf20: chore: regenerate the testing reference at the top of the authentication-budget stack (#1485) (@jmrplens)
  • e3e09d2: chore: regenerate the testing reference at the top of the configuration stack (#1525) (@jmrplens)
  • e5b0357: chore: update manifests for v3.3.0 (@jmrplens)
  • 7bab2ea: ci(release): open every release with a written summary, and refuse a tag without one (#1555) (@jmrplens)
  • 450f926: ci(release): prove each release binary rebuilds bit for bit, and honor DESTDIR in install.sh (#1557) (@jmrplens)
  • d6a58ea: ci(release): publish to PyPI with steps of our own, from a hash-pinned lock, under the trusted publisher (#1551) (@jmrplens)
  • 3da05d1: ci: adopt the Developer Certificate of Origin (#1562) (@jmrplens)
  • 2512c41: ci: fuzz weekly, measure the tree's branch and condition coverage, and date the linter count (#1558) (@jmrplens)
  • 3f25d21: test(e2e): commit the suite's pipeline configurations with the skip marker, and gate it (#1462) (@jmrplens)
  • 0506a76: test(e2e): keep one surface's description edit from resolving the next surface's to-do (#1464) (@jmrplens)
  • e920d95: test(e2e): run the throttle probe last on the complete CE run's stack, and fix a branch-commit flake (#1646) (@jmrplens)
  • a691865: test(e2e): wait for the scheduled storage move to end before cleanup (#1465) (@jmrplens)
  • 8f339f4: test(orbit): move the fixtures into the jmrp-oss open source group and re-record the Orbit routes (#1532) (@jmrplens)
  • 6049193: test(orbit): use the renamed fixture group and re-record the Orbit routes before GA (#1529) (@jmrplens)
  • 276918c: test(server): give the parked listing a registration's bound under the race detector (#1649) (@jmrplens)

Full changelog: v3.3.0...v3.4.0

Verify the assets

All binaries are listed in checksums.txt, signed keylessly with cosign:

cosign verify-blob \
  --bundle checksums.txt.sigstore.json \
  --certificate-identity "https://github.com/jmrplens/gitlab-mcp-server/.github/workflows/release.yml@refs/tags/v3.4.0" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  checksums.txt
sha256sum --check --ignore-missing checksums.txt