Call GitLab projects, issues, merge requests, and pipelines with OAuth or a personal access token.
- Other
- gitlab
- api
- oauth
- pat
- issues
- merge-requests
- pipelines
- multi-account
- License
- MIT
- Published
- August 22, 2026
- Pinned commit
d63169f- Rating
- No ratings yet
- Forks
- 0
- Stars
- 0
- Adaptation effort
- —
README
@kody/gitlab
Intent
Reusable GitLab REST helpers for Kody agents. One package covers bring-your-own OAuth applications and personal access tokens for projects, issues, merge requests, pipelines, and generic REST. Multi-account routing is an integrationName (OAuth) or secretName (PAT) parameter — there are no hard-coded personal aliases or project paths.
What this package does
After you connect GitLab to Kody, agents can:
- Call the GitLab REST API with the correct saved credential (
request,paginate) - Verify which GitLab identity will perform an action (
get-current-user) - List and fetch projects, issues, merge requests, and pipelines
- Preview or apply writes (
issues/create,issues/update,merge-requests/*,pipelines/*)
It does not register a GitLab OAuth application for you. There is no built-in GitLab platform app.
Share this listing at https://kody.codes/@kody/gitlab
Auth lanes
| Lane | Credential | When to use |
|---|---|---|
| A. Personal access token | User secret (documented name gitlabAccessToken) passed as secretName | Fastest for many automations; best for read-only work via read_api. |
| B. Bring-your-own OAuth App | Saved integration named gitlab (or another name you pass as integrationName) | When you want refreshable OAuth instead of a PAT. |
Required host for gitlab.com: gitlab.com. Self-hosted instances use that instance host instead.
secretName selects the PAT lane and ignores integrationName.
Lane A: personal access token
- Open GitLab personal access tokens and create a token. Use
read_apifor read-only helpers, orapifor writes. - Copy the token once. Never paste it into chat.
- Save it in Kody and approve the host:
https://kody.codes/account/secrets/new?name=gitlabAccessToken&description=GitLab%20personal%20access%20token&allowedHosts=gitlab.com&scope=user- Call helpers with
secretName: 'gitlabAccessToken'(or your chosen secret name).
Lane B: bring-your-own OAuth application
There is no built-in GitLab OAuth app. Create one, then connect it.
- Open GitLab applications → Add new application.
- Set the Redirect URI exactly to
https://kody.codes/connect/oauth(self-hosted Kody uses that deployment origin plus/connect/oauth). - Enable Confidential and the
apiscope (read_apiplusread_useris enough for read-only helpers). - Copy the application id and secret. Never paste the secret into chat.
- Connect:
https://kody.codes/connect/oauth?provider=gitlab&authorizeUrl=https%3A%2F%2Fgitlab.com%2Foauth%2Fauthorize&tokenUrl=https%3A%2F%2Fgitlab.com%2Foauth%2Ftoken&flow=confidential&scopes=api&allowedHosts=gitlab.com&apiBaseUrl=https%3A%2F%2Fgitlab.com%2Fapi%2Fv4&dashboardUrl=https%3A%2F%2Fgitlab.com%2F-%2Fprofile%2FapplicationsDecoded: authorize https://gitlab.com/oauth/authorize, token https://gitlab.com/oauth/token, flow=confidential, scopes api, host gitlab.com. Paste the client ID and secret into the Kody setup form, then authorize GitLab.
Default helper input: omit integrationName and secretName. The package uses OAuth integration gitlab.
Reconnect later at https://kody.codes/connect/oauth?provider=gitlab (or ?provider=<your-integration-name>).
Self-hosted GitLab
Pass instanceUrl (origin) or apiBaseUrl (https://gitlab.example.com/api/v4) on every call. Approve that instance host on the secret or OAuth connect URL.
OAuth for another instance (change provider for extra accounts):
https://kody.codes/connect/oauth?provider=gitlab&authorizeUrl=https%3A%2F%2Fgitlab.example.com%2Foauth%2Fauthorize&tokenUrl=https%3A%2F%2Fgitlab.example.com%2Foauth%2Ftoken&flow=confidential&scopes=api&allowedHosts=gitlab.example.com&apiBaseUrl=https%3A%2F%2Fgitlab.example.com%2Fapi%2Fv4PAT for another instance: use the same /account/secrets/new URL with allowedHosts set to that host.
Multiple GitLab identities
Every export accepts optional integrationName (OAuth, default gitlab) and secretName (PAT). Connect extra accounts under their own integration names — for example gitlab-work — by changing provider in the connect URL. Pass that name on every call. Do not hard-code personal aliases in forks of this package.
import getGitlabCurrentUser from 'kody:@kody/gitlab/get-current-user'
export default async function main() {
return await getGitlabCurrentUser({ integrationName: 'gitlab-work' })
}account is accepted as an alias for integrationName.
Mutations and dryRun
Create, update, merge, retry, cancel, and REST methods other than GET / HEAD / OPTIONS require:
dryRun: true— validate and return a preview; GitLab is not writtenconfirm: true— perform the live write
import createGitlabIssue from 'kody:@kody/gitlab/issues/create'
export default async function main() {
return await createGitlabIssue({
project: 'example-group/example-project',
title: 'Example',
dryRun: true,
})
}Smoke tests
Run these from execute after connect (or after PAT host approval). Prefer packages.invoke against a fork of this package so secret mounts and OAuth run in package runtime.
Package overview (no credentials)
import describeGitlab from 'kody:@kody/gitlab'
export default async function main() {
return await describeGitlab()
}Identity (Lane B — OAuth)
import getGitlabCurrentUser from 'kody:@kody/gitlab/get-current-user'
export default async function main() {
return await getGitlabCurrentUser()
// or: getGitlabCurrentUser({ integrationName: 'gitlab' })
}A successful response includes username for the connected GitLab user.
Raw OAuth equivalent (integration bootstrap):
import { createAuthenticatedFetch } from 'kody:runtime'
export default async function main() {
const gitlabFetch = await createAuthenticatedFetch('gitlab')
const response = await gitlabFetch('https://gitlab.com/api/v4/user')
if (!response.ok) {
throw new Error(`GitLab smoke test failed: ${response.status} ${await response.text()}`)
}
const user = (await response.json()) as { username: string }
return { username: user.username }
}Identity (Lane A — PAT)
import getGitlabCurrentUser from 'kody:@kody/gitlab/get-current-user'
export default async function main() {
return await getGitlabCurrentUser({ secretName: 'gitlabAccessToken' })
}Raw PAT equivalent:
export default async function main() {
const response = await fetch('https://gitlab.com/api/v4/user', {
headers: {
'PRIVATE-TOKEN': '{{secret:gitlabAccessToken}}',
},
})
if (!response.ok) {
throw new Error(`GitLab smoke test failed: ${response.status} ${await response.text()}`)
}
const user = (await response.json()) as { username: string }
return { username: user.username }
}Mutation preview
import gitlabRequest from 'kody:@kody/gitlab/request'
export default async function main() {
return await gitlabRequest({
method: 'POST',
path: '/projects/example-group%2Fexample-project/issues',
body: { title: 'Example', description: 'Preview only' },
dryRun: true,
})
}Dynamic invoke
Prefer static kody:@kody/gitlab/... imports. Use packages.invoke when the export name is data or you need this package's runtime:
import { packages } from 'kody:runtime'
export default async function main() {
return await packages.invoke({
kodyId: 'gitlab',
exportName: './get-current-user',
params: {},
})
}Pass the bare kody id gitlab, not @kody/gitlab.
Exports
kody:@kody/gitlab— package overview and setup URLskody:@kody/gitlab/accounts— OAuth vs PAT lane guidancekody:@kody/gitlab/get-current-user— identity smoke testkody:@kody/gitlab/paginate— REST pagination helperkody:@kody/gitlab/request— REST request helperkody:@kody/gitlab/types— shared TypeScript typeskody:@kody/gitlab/projects/list— list projectskody:@kody/gitlab/projects/get— fetch one projectkody:@kody/gitlab/issues/list— list issueskody:@kody/gitlab/issues/get— fetch one issuekody:@kody/gitlab/issues/create— create an issue (dryRun/confirm)kody:@kody/gitlab/issues/update— update an issue (dryRun/confirm)kody:@kody/gitlab/merge-requests/list— list merge requestskody:@kody/gitlab/merge-requests/get— fetch one merge requestkody:@kody/gitlab/merge-requests/create— create a merge request (dryRun/confirm)kody:@kody/gitlab/merge-requests/update— update a merge request (dryRun/confirm)kody:@kody/gitlab/merge-requests/merge— merge a merge request (dryRun/confirm)kody:@kody/gitlab/pipelines/list— list pipelineskody:@kody/gitlab/pipelines/get— fetch one pipelinekody:@kody/gitlab/pipelines/create— create a pipeline (dryRun/confirm)kody:@kody/gitlab/pipelines/retry— retry a pipeline (dryRun/confirm)kody:@kody/gitlab/pipelines/cancel— cancel a pipeline (dryRun/confirm)
Project locators: { project }, { projectId }, { projectPath }, or { projectUrl }. Issue / merge-request / pipeline locators also accept the matching *Url or iid aliases.
Examples
REST read (default OAuth gitlab):
import gitlabRequest from 'kody:@kody/gitlab/request'
export default async function main() {
const response = await gitlabRequest({
path: '/projects',
query: { membership: true, per_page: 5 },
throwOnError: true,
})
return response.data
}Project issues with an extra OAuth identity:
import listGitlabIssues from 'kody:@kody/gitlab/issues/list'
export default async function main() {
return await listGitlabIssues({
project: 'example-group/example-project',
state: 'opened',
integrationName: 'gitlab-work',
})
}Troubleshooting
The redirect_uri MUST match the registered callback URL: callback must be exactlyhttps://kody.codes/connect/oauth.401 Unauthorizedwith a PAT: expired token, missing scope, or a stray space. Rotate at GitLab token settings and update the secret.- Self-hosted 401 / host-approval errors: approve the instance host on the secret or OAuth connect URL, and pass
instanceUrlorapiBaseUrl. - Token exchange fails: GitLab confidential apps require the client secret. Regenerate the secret and reconnect.
- Writes rejected with a mutation-guard error: pass
dryRun: trueorconfirm: true.
Report this listing
Log in to report this listing.