Skip to main content

gglint

CI PyPI License

Keep the MCP servers of every coding agent in your repository in sync.

Claude Code, Codex, Cursor and OpenCode each read MCP servers from their own config file, in their own format. In a repository set up for more than one of them, those files drift apart: a server is added to .mcp.json but not to .cursor/mcp.json, a version is pinned in one file and not the other, or an agent has no MCP config at all. gglint finds every difference and adds the missing servers for you, written in each agent's own format.

gglint mcp drawing each MCP server in Claude Code, Codex, Cursor and OpenCode as a table, then listing six problems: missing servers, servers set up differently, and an agent without a config file

  • One view of every agent. A table shows which agent gets which server, and how each one differs.
  • Safe fixes. --fix only adds servers. It never changes or removes an existing entry, and keeps your comments and formatting. --overwrite, when you ask for it, makes each server start with the same command and arguments everywhere.
  • Each agent's own format. A server is translated between JSON and TOML, between each agent's syntax for environment variables, and between local and remote server types.
  • Fast and read-only by default. gglint reads a handful of files at fixed paths and finishes in under a millisecond. It never starts a server or downloads anything.
  • Built for CI. It exits with status 1 when it finds a problem, and --format lines prints one problem per line.

In November 2026 we checked the 258 most-starred GitHub repositories set up for two or more of these agents. Of the 179 that configure MCP servers, 128 (72%) had at least one of the problems gglint reports.

Quick start

From the root of a repository, run gglint with uv. Nothing needs to be installed first:

uvx gglint mcp          # report every problem
uvx gglint mcp --fix    # add the missing servers
git diff                # review what changed

Without uv, pipx run gglint mcp does the same.

Installation

To install gglint and run it as gglint:

uv tool install gglint
pipx install gglint
pip install gglint

gglint is a single compiled program, published on PyPI with prebuilt wheels for Linux (x86_64 and aarch64, glibc and musl), macOS (Intel and Apple silicon) and Windows (x64 and ARM64). Any Python 3.8 or later can install it.

To build it from source, install a Rust toolchain (1.88 or later) and run:

cargo install --git https://github.com/corridor/gglint --locked

Usage

gglint mcp [--fix [--create] [--overwrite]] [--format table|lines] [PATH]
gglint mcp --servers [PATH]
Option Effect
(none) Report every problem and draw each server in each agent.
--fix Add the servers missing from existing config files, then report what's left.
--fix --create Also create the config file of an agent that has its own directory but no MCP config.
--fix --overwrite Also make each server start with the same command and arguments in every agent.
--format lines Print one line per problem instead of the table, for editors and CI.
--servers List every configured server, file by file, without comparing them.
PATH The repository's root. Defaults to the current directory.
Exit status Meaning
0 Every agent gets the same servers, set up the same way.
1 gglint found a problem, one is left after --fix, or there are no MCP config files.

Colours are used when standard output is a terminal and NO_COLOR isn't set. The summary line, with counts and timing, goes to standard error.

What gglint checks

Rule Reported when Fixed by
missing-server A server in one agent's config file is missing from another agent's. --fix
missing-config An agent has its own directory but no MCP config file, while other agents have servers. --fix --create
different-server The same server runs another version or has other arguments. --fix --overwrite
The same server runs another program or URL, has other environment variables or headers, or is turned off in one agent. Reported only
renamed-server The same server has a different name in some agents. Reported only

Differences in how a server is set up are sometimes deliberate, such as each agent running its own runtime or port, so --fix only reports them. When the command lines should match, --overwrite makes them match; everything else is left to you.

Matching servers. Servers are matched by name, then by what they run, so a server named differently in one agent is still recognised as the same server. The way a package is launched doesn't count as a difference:

  • npx -y pkg, npx --yes pkg@latest, pnpm dlx pkg, yarn dlx pkg, bunx pkg and bun x pkg all run the npm package pkg.
  • uvx pkg, uv tool run pkg and pipx run pkg all run the PyPI package pkg.
  • npx mcp-remote <url> reaches the remote server at <url>.
  • No version and latest both mean the newest version.
  • ${KEY} in Claude Code and ${env:KEY} in Cursor refer to the same environment variable.

Environment variables and headers are compared by name only, since their values can be secrets.

Supported agents

Agent MCP config file Agent directory Format
Claude Code .mcp.json .claude/ JSON, mcpServers
Codex .codex/config.toml .codex/ TOML, [mcp_servers.<name>]
Cursor .cursor/mcp.json .cursor/ JSON, mcpServers
OpenCode opencode.json, opencode.jsonc .opencode/ JSON with comments, mcp.servers or mcp

gglint compares the agents that have an MCP config file or their own directory at the repository's root. A CLAUDE.md, .cursorrules or AGENTS.md file on its own doesn't count, because it doesn't show that the agent is set up in the repository.

How fixing works

--fix adds and never changes or removes anything that is already written.

  • A missing server is copied from the first config file where it's turned on, in the order Claude Code, Codex, Cursor, OpenCode, and added to each config file that lacks it.
  • A config file is created only with --create, and only for an agent with its own directory. It receives every server that is turned on in the other agents.
  • A command line is changed only with --overwrite. A server that runs another version of the same program, or the same program with other arguments, gets the command and arguments of that same first config file. Its environment variables, headers, on/off state and keys only one agent has, such as Codex's cwd, are kept. Review the result with git diff: a difference such as a runtime chosen per agent, or a path that starts with Cursor's ${workspaceFolder}, may have been deliberate.
  • JSON is edited with jsonc-parser and TOML with toml_edit, so comments and formatting are kept.
  • Running --fix a second time changes nothing.
  • A file excluded by .gitignore is still updated, and marked gitignored in the output, because the change won't be committed.

References to environment variables are rewritten in each agent's syntax, in values and in command lines:

Agent Environment variable KEY
Claude Code ${KEY}
Cursor ${env:KEY}
OpenCode {env:KEY}
Codex env_vars = ["KEY"], env_http_headers, or bearer_token_env_var for Authorization: Bearer

Remote servers get "type": "http" in Claude Code, or "sse" for a URL ending in /sse. OpenCode servers get "type": "local" or "remote", with the command and its arguments in one list.

A server is skipped, and listed under Not added:, when its setup can't be expressed in the other agent's format: for example Claude Code's ${KEY:-default}, Cursor's ${workspaceFolder}, or an environment variable inside a command line, which Codex doesn't expand.

Example

$ gglint mcp --fix
Changed 3 files:
  .mcp.json            added linear
  .codex/config.toml   added github, playwright, linear
  .cursor/mcp.json     added github

Review the changes with `git diff`.

The same github server, written for Codex and Cursor:

# .codex/config.toml
+[mcp_servers.github]
+url = "https://api.githubcopilot.com/mcp/"
+bearer_token_env_var = "GITHUB_TOKEN"

# .cursor/mcp.json
+    "github": {
+      "url": "https://api.githubcopilot.com/mcp/",
+      "headers": {
+        "Authorization": "Bearer ${env:GITHUB_TOKEN}"
+      }
+    }

With --overwrite, Cursor's playwright also starts as in Claude Code:

$ gglint mcp --fix --overwrite
Changed 3 files:
  .mcp.json            added linear
  .codex/config.toml   added github, playwright, linear
  .cursor/mcp.json     added github; updated playwright
# .cursor/mcp.json
-      "args": ["-y", "@playwright/mcp@latest"]
+      "args": ["-y", "@playwright/mcp@0.0.78", "--headless"]
$ gglint mcp --fix --create
Changed 1 file:
  opencode.json (new)   added github, playwright, linear

Output formats

--format lines prints one problem per line: where it is, the rule, and a description, with the server's other locations indented below it.

$ gglint mcp --format lines
./.mcp.json:3:5  missing-server   `github` is set up for Claude Code but not for Codex (.codex/config.toml) or Cursor (.cursor/mcp.json)
./.mcp.json:8:5  different-server `playwright` runs different versions of the npm package `@playwright/mcp`: 0.0.78 in Claude Code; the newest in Cursor
    also at ./.cursor/mcp.json:3:5
./.mcp.json:3:5  missing-config   OpenCode has a .opencode directory but no opencode.json, so it gets none of the 3 MCP servers set up for Claude Code and Cursor

--servers lists every server as it's written. Environment variables and headers are shown by name, never by value.

$ gglint mcp --servers
./.mcp.json:3:5  claude-code  github  https://api.githubcopilot.com/mcp/  (headers=[Authorization])
./.mcp.json:8:5  claude-code  playwright  npx -y @playwright/mcp@0.0.78 --headless
./.cursor/mcp.json:3:5  cursor       playwright  npx -y @playwright/mcp@latest
./.cursor/mcp.json:7:5  cursor       linear  https://mcp.linear.app/sse

Continuous integration

Fail a pull request when the agents' MCP servers drift apart:

# .github/workflows/mcp.yml
name: MCP servers
on: [pull_request]

jobs:
  gglint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pipx run --spec gglint==0.1.1 gglint mcp --format lines

Security

  • Read-only by default. Without --fix, gglint reads files and writes nothing.
  • Nothing is executed or fetched. MCP servers aren't started, packages aren't resolved, and environment variables aren't expanded. It's safe to run on an untrusted repository.
  • Secrets stay out of the output. Environment variables and headers are printed by name only.
  • One external program. During --fix, gglint runs git check-ignore to mark files your .gitignore excludes.

Limitations

  • Servers added by Claude Code plugins, enabled in .claude/settings.json, aren't read. A server provided by a plugin can be reported as missing from Claude Code.
  • User-level settings such as ~/.claude.json or ~/.codex/config.toml aren't read.
  • Only config files at the repository's root are read, so each package of a monorepo is checked separately.
  • VS Code and GitHub Copilot (.vscode/mcp.json) and Gemini CLI (.gemini/settings.json) aren't supported yet.
  • Codex only reads .codex/config.toml in projects you have marked as trusted.

Development

cargo test                          # unit and integration tests
cargo clippy --all-targets          # lints
cargo fmt                           # formatting

tests/corpus.rs runs --fix against a directory of real repositories and checks that nothing existing is removed or changed, that config files are created only with --create and only in an agent's directory, that no file becomes unreadable, that problems never increase, and that a second run changes nothing:

GGLINT_CORPUS=path/to/clones cargo test --release --test corpus -- --ignored --nocapture

The code lives in src/harness/: mcp.rs reads each agent's config, compare.rs finds the problems, and fix.rs writes the fixes.

Releasing

  1. Update version in Cargo.toml and add the release to CHANGELOG.md.
  2. Merge to main.
  3. Publish a GitHub release tagged v<version>, for example v0.1.0.

The release workflow builds and tests every wheel, publishes them to PyPI, and attaches them to the release.

License

gglint is licensed under the Apache License 2.0. The licenses of the libraries compiled into it are listed in THIRD_PARTY_LICENSES.

Metadata

Release files for gglint 0.1.1

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

Source distribution (sdist)

Source distribution for gglint 0.1.1
File Size Uploaded Provenance Actions
gglint-0.1.1.tar.gz 58.9 kB With provenance Details for gglint-0.1.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for gglint 0.1.1
File Actions
gglint-0.1.1-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details for gglint-0.1.1-py3-none-win_arm64.whl
gglint-0.1.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for gglint-0.1.1-py3-none-win_amd64.whl
gglint-0.1.1-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details for gglint-0.1.1-py3-none-musllinux_1_2_x86_64.whl
gglint-0.1.1-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details for gglint-0.1.1-py3-none-musllinux_1_2_aarch64.whl
gglint-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for gglint-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
gglint-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for gglint-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
gglint-0.1.1-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for gglint-0.1.1-py3-none-macosx_11_0_arm64.whl
gglint-0.1.1-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details for gglint-0.1.1-py3-none-macosx_10_12_x86_64.whl

Total release size: 4.1 MB

Release files / gglint-0.1.1.tar.gz

Download URL gglint-0.1.1.tar.gz
Size 58.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d064f7bfd083aec79e2e51eb9d2eedd3f391b4f36aa9b3e8448b0513742a8142
BLAKE2b-256 checksum
How to use checksums
657041d8c3f17f88f7728310fac33cf76d584f19cdbae2fa57e55e0029260671
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-win_arm64.whl

Download URL gglint-0.1.1-py3-none-win_arm64.whl
Size 451.0 kB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
18a462185462ebc030315204816600f764f0cb4fb2f2f4626ff7336e9e358aaf
BLAKE2b-256 checksum
How to use checksums
e66495cfd7a674502159dfac4248a88d57686bf67dd207fd7513887f572c8849
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-win_amd64.whl

Download URL gglint-0.1.1-py3-none-win_amd64.whl
Size 484.1 kB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
de120e0f8f8c4290081adcd64bed54c6720aaaf8dc77445aa567899edd7e1425
BLAKE2b-256 checksum
How to use checksums
3054630bbca9abd13cd6f3aa3949b01ab44438b37059045874b573e082c1520b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-musllinux_1_2_x86_64.whl

Download URL gglint-0.1.1-py3-none-musllinux_1_2_x86_64.whl
Size 582.0 kB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
0c430398b9c12172f921863e2e6d50d827c8fac5c35ec23ee11dd37b0aadeaf2
BLAKE2b-256 checksum
How to use checksums
63e2e93b941aafe28e6a47a905173ee715e7d0d4eb7398578e4ecf70d9ca2bdf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-musllinux_1_2_aarch64.whl

Download URL gglint-0.1.1-py3-none-musllinux_1_2_aarch64.whl
Size 541.4 kB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
67c1e4279eba2a03a3d1337b4d277a171ea63d1354712c54bcb7c37357285ee6
BLAKE2b-256 checksum
How to use checksums
2249b2a85642a808431118299d9402e7ea7498e91faf3eb39adf53e1156d808d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL gglint-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 533.2 kB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
c5ced586b41c3dc2b45425e3a82145251f98b2de25f5a5bacf795ba49b91764d
BLAKE2b-256 checksum
How to use checksums
d14c4a2bbc69dbb2097d927d34e4fddcb70ef6c0ee48502958b0bd1d8ca37670
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL gglint-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 495.6 kB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
4061de47ef4c35401431ebf085c37fb97a32afa3645ce5172b8c42c36a51ea5f
BLAKE2b-256 checksum
How to use checksums
ac233eee8e9c67d0f1903fc0a35ffdab64541fd700436094ecf94290584c2121
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-macosx_11_0_arm64.whl

Download URL gglint-0.1.1-py3-none-macosx_11_0_arm64.whl
Size 481.3 kB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
55ff3ec713625c01a86297896e62f321c55461fb5c7c471f3baea0317e33601a
BLAKE2b-256 checksum
How to use checksums
6091e81f813574872d1a17b89087ea8ea1b7acc2c05691b51bcc4a7dff1e7bff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / gglint-0.1.1-py3-none-macosx_10_12_x86_64.whl

Download URL gglint-0.1.1-py3-none-macosx_10_12_x86_64.whl
Size 512.8 kB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
3304eee0bcbfb406707f0e7381efe657d7ac68893d0466818e22daba05ff022a
BLAKE2b-256 checksum
How to use checksums
a40468c5c986e2de74eaf288e77e1a79c600ef019d5238a56623412782b4940e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Security for gglint 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 9 files in this release have been uploaded with provenance metadata.

Select from 9 files

Attestation for gglint-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

9 release files Full provenance

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