Skip to main content

monorepoly

Org-wide index of code and engineering activity for LLM agents.

Install

  • Users of a hosted server: uv tool install monorepoly (or pip install monorepoly), then monorepoly setup --server_url <url> --client_id <id>. Nothing else is installed.
  • Running the server yourself: monorepoly[server]; running the indexer: monorepoly[indexer]. A command whose extra is missing says which one to install.
  • Images: the Dockerfile's server target runs serve_http, its indexer target (the default) the indexing commands.

Setup

  1. docker compose up -d (Atlas Local, for tests and local runs)
  2. Set GITLAB_GROUP_ID (a group path or numeric id), GITLAB_TOKEN, GITLAB_URL, GOOGLE_CLOUD_PROJECT, MONGODB_DB, MONGODB_URI, TYPESAFE_API_KEY
  3. gcloud auth application-default login

Use

  • Index every project in the group: uv run monorepoly index_org (archived projects are skipped; add --include_archived to index them too)

  • Index one project's code, commits and branches: uv run monorepoly index_project group/project

  • Sync one project's issues, merge requests and discussions: uv run monorepoly sync_activity group/project

  • --debug skips every database write, but still makes the paid Vertex AI and Jev calls.

  • Set up Claude Code: monorepoly setup prompts for the settings, checks GitLab, MongoDB and Google credentials, installs the skill and registers the server (--debug changes nothing and prints the claude mcp add command with secrets masked)

  • Or register by hand with your own GitLab token, passing every setting with -e (see Security model):

    claude mcp add monorepoly -e GITLAB_GROUP_ID=... -e GITLAB_TOKEN=... -e GITLAB_URL=... \
      -e GOOGLE_CLOUD_PROJECT=... -e MONGODB_DB=... -e MONGODB_URI=... -e TYPESAFE_API_KEY=... \
      -- uv run --directory <this repo> monorepoly serve
    
  • Skill: monorepoly install-skill copies it to ~/.claude/skills/; rerun after upgrading

  • Connect to a hosted server (see Self-host on Google Cloud): monorepoly setup --server_url <url> --client_id <GitLab OAuth application id>; Claude Code opens GitLab in the browser on first use

Index from CI

The image runs the CLI; its entrypoint is the monorepoly command.

docker build --target indexer -t registry.gitlab.com/<group>/monorepoly:latest .
docker push registry.gitlab.com/<group>/monorepoly:latest

The job authenticates to Vertex AI with Workload Identity Federation: GitLab issues the job an OIDC token, and GCP exchanges it for a service account. No key is stored anywhere.

One-time GCP setup:

  1. Create a workload identity pool and an OIDC provider for https://gitlab.com, with the attribute mapping google.subject=assertion.sub,attribute.namespace_path=assertion.namespace_path and the attribute condition assertion.namespace_path.startsWith('<group>'), so only your group's pipelines can use it.

  2. Grant a service account roles/aiplatform.user, and grant the pool's principals roles/iam.workloadIdentityUser on that service account.

  3. Generate the credential config (it holds no secret):

    gcloud iam workload-identity-pools create-cred-config \
      projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider> \
      --service-account=<sa>@<project>.iam.gserviceaccount.com \
      --credential-source-file=/tmp/gcp-id-token --output-file=gcp-credentials.json
    

    and store its content in the CI variable GCP_CREDENTIAL_CONFIG.

CI variables (masked): GITLAB_GROUP_ID, GITLAB_TOKEN (a group access token with read_api and read_repository), GITLAB_URL, GOOGLE_CLOUD_PROJECT, MONGODB_DB, MONGODB_URI, TYPESAFE_API_KEY.

monorepoly:
  image:
    name: registry.gitlab.com/<group>/monorepoly:latest
    entrypoint: [""]
  id_tokens:
    GCP_ID_TOKEN:
      aud: https://iam.googleapis.com/projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>
  variables:
    GOOGLE_APPLICATION_CREDENTIALS: /tmp/gcp-credentials.json
  script:
    - echo "$GCP_ID_TOKEN" > /tmp/gcp-id-token
    - echo "$GCP_CREDENTIAL_CONFIG" > "$GOOGLE_APPLICATION_CREDENTIALS"
    - monorepoly index_project "$CI_PROJECT_PATH"
    - monorepoly sync_activity "$CI_PROJECT_PATH"
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

A CI runner keeps no clone cache, so every run clones the repository again; only commits new since the last run reach Jev and Vertex AI.

Self-host on Google Cloud

deploy/ is a Pulumi program that runs the MCP server as a Cloud Run service and index_org as a Cloud Run job, with every credential in Secret Manager. Engineers connect over HTTP and log in with GitLab; their laptops hold no MongoDB, Vertex AI or TypeSafe credentials.

You need: a GCP project, gcloud auth application-default login, Pulumi and an Atlas cluster. The images come from ghcr.io, built by GitHub Actions on every push to master and tagged with the commit; a deploy runs the images of the checkout's HEAD, so push and wait for the images workflow before pulumi up.

  1. In GitLab, create an OAuth application (group or instance level): scope read_api, redirect URI http://localhost:41593/callback, Confidential unchecked (Claude Code uses PKCE, so there is no secret). Its application id is public; you hand it to users.

  2. In Atlas, create two database users for the index database: read-only for the server, read-write for the indexer.

  3. Create a group access token with read_api and read_repository for the indexer.

  4. Configure and deploy, keeping the state in your own project:

    cd deploy
    pulumi login gs://<state bucket>
    pulumi stack init prod
    pulumi config set gcp:project <project>
    pulumi config set gcp:region <region>
    pulumi config set gitlabGroupId <group path or id>
    pulumi config set mongodbDb <database>
    pulumi config set --secret gitlabToken <group access token>
    pulumi config set --secret mongodbUriReadonly <read-only URI>
    pulumi config set --secret mongodbUriReadwrite <read-write URI>
    pulumi config set --secret typesafeApiKey <key>
    pulumi up
    

    Optional: gitlabUrl (default https://gitlab.com), schedule (default hourly, 0 * * * *), indexerTimeout (default 3300s; keep it below the schedule's interval, so runs never overlap), indexerMemory (default 4Gi; every run clones all repositories into memory), license (see License), egressSubnetwork (sends all traffic through that subnet, e.g. one whose Cloud NAT has a static IP on the Atlas IP access list), schedulerRegion (default gcp:region; Cloud Scheduler is missing from some regions, e.g. europe-north1), imageTag (default the checkout's commit), imageRepository (default kimvais/monorepoly).

  5. Run the first full index by hand; it can take hours: gcloud run jobs execute monorepoly-index --region <region> --task-timeout=24h --wait

  6. Start the schedule: pulumi config set scheduleEnabled true && pulumi up

  7. Give users the setup_command output; set oauthClientId to your OAuth application id to have it filled in.

Every user sees only what their own GitLab account can see, as with a local server.

License

Free for public repositories and open-source projects; private repositories and commercial use need a commercial license (terms: LICENSE).

Public projects are indexed without a license. Private and internal projects need a license file covering their group; without one the indexer skips them and stores nothing about them, not even their names.

Give the indexer the file's content in MONOREPOLY_LICENSE. On Google Cloud: pulumi config set --secret license -- "$(cat monorepoly.license)" && pulumi up.

A license file is TOML followed by an SSH signature over exactly the bytes above it. To issue one, write the payload, with key set to the fingerprint ssh-keygen -l -f <key>.pub prints:

licensee = "Acme Oy"
gitlab_url = "https://gitlab.com"
groups = ["acme"]
expires = 2027-10-16
key = "SHA256:L2Z9F/e49TkoYH8+dWjsfpjJvuyJgDnkFLOOVlJDtpQ"

then sign it with a key whose .pub file is in monorepoly/trusted_keys/, and append the signature:

ssh-keygen -Y sign -f ~/.ssh/id_ed25519 -n monorepoly-license acme.toml
cat acme.toml acme.toml.sig > monorepoly.license

The file must keep LF line endings: any changed byte breaks the signature. groups are full paths and include their subgroups; expires is the last valid day.

Security model

  • The server filters every query by what your GitLab token can see: projects, per-feature access (repository, merge requests, issues) and confidential items. This protects against accidental exposure, not against anyone holding MONGODB_URI: the database holds the whole group's index, and the URI reads all of it directly.
  • Give the server a read-only Atlas database user; only the indexer needs write access.
  • The hosted server is public at the network level on purpose: it checks every request's GitLab token with GitLab, and accepts any of the user's tokens with read_api, which already reads everything the index would show them.
  • Distribute the server settings only to people entitled to read the whole group.
  • Register the settings with claude mcp add -e KEY=VALUE ... rather than in your shell profile, so an agent's shell commands cannot read them.

Test

uv run pytest (needs Atlas Local). Live checks: uv run pytest -m live.

Metadata

Release files for monorepoly 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for monorepoly 0.2.0
File Size Uploaded
monorepoly-0.2.0.tar.gz 52.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for monorepoly 0.2.0
File Interpreter ABI Platform
monorepoly-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 114.5 kB

Release files / monorepoly-0.2.0.tar.gz

Download URL monorepoly-0.2.0.tar.gz
Size 52.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f3c239424f9af284d343b5fe482878b090bf4a3455af83d61b495c727e69819a
BLAKE2b-256 checksum
How to use checksums
b6d62c6b543a6d1a18dda415536b8c563572ec2e6f5658cbff43e7b738a43f9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / monorepoly-0.2.0-py3-none-any.whl

Download URL monorepoly-0.2.0-py3-none-any.whl
Size 61.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ec2fe368a371b135fa673ce8596a965b4286782b686bcc86de3fcc52fa8b157f
BLAKE2b-256 checksum
How to use checksums
0c8c30eccdf5926378f8504fe479ed980b7a1af797337111377c2b3a290c75c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page