tccutil.py: editing the macOS accessibility database from the command line
♿️ Command line tool to modify OS X's accessibility database (TCC.db)
At a glance
- What is it?
- Apple ships tccutil in /usr/bin, and it can only reset the entire privacy database. tccutil.py is a Python script that adds the missing verbs, listing, inserting, removing, enabling and disabling individual entries, and it understands the difference between the system-wide database and a single user's own. It needs System Integrity Protection turned off, and the README says plainly that the risk of doing that is up to you.
- Who is it for?
- Adopt tccutil.py if you are automating macOS machines and need to grant or revoke one accessibility permission without wiping every privacy decision on the system, since Apple's own tool can only reset the whole database.
- Can I use it commercially?
- Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 23 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap Apple's utility leaves
macOS keeps its privacy decisions in a database called TCC.db, and Apple's own command line tool for it lives in /usr/bin. The README's complaint is that tccutil only supports one command, which is to reset the entire database, and that it has been like this for many versions of macOS. The author wanted a utility that could add, remove, list and take other actions.
The distinction between reset everything and change one entry is the whole value of the project. Apple's tool throws away every permission decision on the machine and makes you re-grant them through the user interface, one application at a time, by opening System Settings and clicking. tccutil.py lets you target one bundle identifier or path, which is what you need when a script has to drive the accessibility API on a machine nobody is sitting at.
That use case is more common than it sounds. A build server running UI tests, a provisioning script that grants a helper tool screen recording access, a developer restoring permissions on a fresh machine. In each case the manual path through System Settings is the bottleneck, and the reset-everything alternative is unacceptable because it destroys the other permissions too.
The primary language is Python, the licence is GPL-2.0, the default branch is main, and the recent releases are v1.5.5 from 2026-09-30, v1.5.2 from 2026-09-27 and v1.5.1 from 2025-10-03. The last push was on 2026-09-30 with the project not archived.
The implementation is a single file. The top-level listing is .github/, .gitignore, LICENSE, README.md, tccutil.py and tests/, and the whole program lives in that one script. The topic list includes hacktoberfest, which explains part of the contributor flow, and homebrew, which is how most people install it.
Global versus user databases, and the privileges each needs
The section on understanding global versus local TCC databases is the one thing every user needs to read before running anything, because it determines whether the command needs sudo and which permissions it will actually change.
macOS maintains two separate databases. The global TCC database is at /Library/Application Support/com.apple.TCC/TCC.db, it holds system-wide permissions that apply to all users, and it requires sudo or admin privileges to modify. It is the one used by default when you run tccutil.py without the --user flag.
The local user TCC database is at ~/Library/Application Support/com.apple.TCC/TCC.db. It holds user-specific permissions and it does not require sudo, because a user can modify their own database. You reach it with the --user flag, for example tccutil.py --list -u.
That distinction has a consequence people find out the hard way. A permission granted in the user database is scoped to one account, and the same application running under a different user account will not have it. Conversely, the global database is the one that matters for a script that runs as a system daemon or under a service account.
The usage section repeats the same rule in terms of what to type. The global TCC database, which is the default, requires super-user privileges and you run it with sudo. The local user TCC database, with the --user flag, does not require sudo and can be run as a regular user.
The --user flag takes an optional user name. With no additional parameter it defaults to the current user, and you can name a specific one, which is what you would use on a machine with several accounts, for example a build account alongside a human one.
So the tool has two distinct privilege levels, and which one you want is a decision rather than a detail. The correct answer for most automation is the user database with no sudo at all, because that is both sufficient and far less invasive.
The command surface
The tool is an argparse program and the README shows its full help output, which is worth reading because it explains the shape of the interface.
The positional ACTION argument is only used to perform a reset using /usr/bin/tccutil, with a pointer to man tccutil for the additional syntax. So the reset path is delegated to Apple's own binary rather than reimplemented, which is the sensible choice: the reset semantics are Apple's to define, and wrapping rather than reimplementing means the behaviour cannot drift.
The options are then the interesting part. --service, with the short form -s, sets the TCC service, which selects which service category such as Accessibility, the thing being granted, the operation applies to. --list and -l lists all entries in the accessibility database. --digest prints the digest hash of the accessibility database. --insert and -i adds the given bundle ID or path to the accessibility database. --remove and -r removes a given bundle ID or path. --enable and -e enables accessibility access for the given bundle ID or path. --disable and -d disables it. --user and -u modifies the accessibility database for a given user, defaulting to the current one. And --version shows the version of the script.
So there are five verbs on the database, list, insert, remove, enable and disable, plus a digest for inspection, plus a service selector that scopes the others.
Two of the options carry a constraint that is easy to miss and worth remembering. Enable and disable operate on an entry that must already exist in the database, so enabling is not the same as inserting, and the README marks this in the examples with must already exist in the Database.
The digest option gives you a fingerprint of the database, so printing it is one way to see whether an edit changed anything, and the test suite compares digests against schemas captured from real Macs.
The examples section then shows the pairs. Listing the global database is sudo tccutil.py --list. Adding a path is sudo tccutil.py -i /usr/bin/osascript, and the README notes that the UNIX-style short option and the long option are equivalent, showing --insert com.apple.ScriptEditor2 as the long form of the same thing.
Removing is sudo tccutil.py --remove com.apple.Terminal, and the enable and disable examples are the same shape with the other verbs.
Installing it, and why the local scope matters more than sudo
Installation is through Homebrew:
brew install tccutilAnd the README makes a point that is easy to miss: depending on how you have your PATH variable set up, you can simply type tccutil instead of the full path, and it will run this utility instead of Apple's. That is a genuine hazard rather than a convenience, because the two commands have overlapping names and only one of them can be on your PATH first.
So after installing, typing tccutil --list will do the thing you want, and typing tccutil reset ALL will do the thing Apple's tool does, because the reset is delegated to /usr/bin/tccutil. If you are working on a script that calls tccutil, you need to know which one it is getting.
The alternative install is to clone the repository and manually copy tccutil.py to /usr/local/bin, or run it from any directory with python /path/to/tccutil.py. There is no dependency to install, which is the first of the stated reasons the tool is different from the alternatives: tccutil.py can be installed without any additional software.
The other stated reasons are an easy to use syntax, support for both system-wide and user-scope manipulation, and the fact that it wraps the native /usr/bin/tccutil tool.
The section on how it differs from other solutions is honest about what the author did not like in the alternatives. The Privacy Manager Services project has other dependencies that need to be installed, and has gone over five years without updates. tccmanager.py uses a .plist to add items, which the author calls inconvenient. There is also go-tccutil, which the author admits he only recently found, tccplus, and DocSystem/tccutil.
The comparison that matters most in practice is the dependencies one. A single Python file with no install step can be dropped onto a machine and run, which is exactly what you need on a system where installing a package is itself a permission change.
The examples in the user-scope section show the difference in practice. Listing the current user's database is tccutil.py --list -u with no sudo at all. Adding a path for the current user is tccutil.py -i /usr/bin/osascript -u. And adding a path for a named user is tccutil.py -i /usr/bin/osascript -u myuser.
The final example is the reset of the system-wide database, sudo tccutil.py reset ALL, which delegates to Apple's binary.
The SIP requirement, and the release process
The SIP notice is short and it is the most important thing in the README. This tool needs SIP disabled in order to function. The risk of doing so is up to you. And a link to a discussion thread on the topic.
System Integrity Protection is the mechanism that makes the TCC database tamper-resistant in the first place. It protects the system's signed code and its security-relevant databases, and a modification that bypasses the protection on one of them is exactly the kind of thing SIP exists to prevent. Disabling it is a deliberate act of trust in the machine, and the README is right to say the risk is the user's to take.
The practical consequence is that this is not a tool you deploy broadly. A user database change for your own account still needs SIP off, so even the least invasive form of use requires the system to be in a weaker state.
The contributing section explains how the project is run, and there are two details that show how the repository is kept tidy. Every pull request into main must bump util_version in tccutil.py, for example from 1.5.3 to 1.5.4, and a required check fails until the new version is higher than the one on main and not already tagged. If another pull request merges first with the same version, you update your branch and bump again.
So the version number is enforced by automation rather than by convention, and the enforcement is a required check rather than a review comment. That is a good pattern for a single-file script where the version is what tells users what they have.
The tests are described next, and they are the most interesting part of the repository. Run the tests with python3 -m unittest discover -s tests -v, and they run tccutil.py against TCC.db schemas captured from real Macs. If your macOS version reports a digest that has no fixture yet, the request is to add one.
That is a clever approach to testing an operating-system tool. The hard part of testing a tool that edits TCC.db is that you need a TCC.db, and the schema changes between macOS versions. Capturing real schemas as fixtures and testing against those means the test suite does not need a Mac at all, and it means each new macOS release has an explicit moment where somebody notices the digest has moved and adds a fixture for it.
Releasing is then a tagging step. After merging, tag the merged commit on main with the version from tccutil.py and push the tag, and the Release workflow publishes a GitHub release for any vX.Y.Z tag on main whose version matches tccutil.py.
The match requirement is what ties the two files together. A tag that does not agree with the version in the script does not produce a release, so the tag and the script cannot disagree.
Editorial conclusion
Adopt tccutil.py if you are automating macOS machines and need to grant or revoke one accessibility permission without wiping every privacy decision on the system, since Apple's own tool can only reset the whole database. Do not adopt it without reading the SIP notice, because the tool needs System Integrity Protection disabled to function and that is a deliberate weakening of the machine's own protections, and prefer the user scope with the --user flag, which needs no sudo at all and touches only one account's database. Before you commit, install it through Homebrew and check which tccutil is first on your PATH, since the names collide and the reset path is delegated to Apple's binary, and if you are contributing, remember that every pull request has to bump util_version in tccutil.py or the required check fails.
Frequently asked questions
What does tccutil.py do that Apple's tccutil does not?
Apple's tccutil in /usr/bin only supports resetting the entire database, and has done for many macOS versions. tccutil.py adds listing, inserting, removing, enabling and disabling individual entries by bundle ID or path, and can print the database digest, so a single permission can be changed without discarding all the others.
How do I install tccutil.py?
Install it with Homebrew using brew install tccutil. The alternative is to clone the repository and copy tccutil.py to /usr/local/bin, or run it from any directory with python /path/to/tccutil.py, and it needs no additional software installed.
What is the difference between the global and local TCC databases?
The global database at /Library/Application Support/com.apple.TCC/TCC.db holds system-wide permissions that apply to all users and requires sudo to modify, and it is the default. The local user database at ~/Library/Application Support/com.apple.TCC/TCC.db holds user-specific permissions, needs no sudo, and is reached with the --user flag, optionally with a user name.
Does tccutil.py require System Integrity Protection to be disabled?
Yes. The README states plainly that the tool needs SIP disabled in order to function and that the risk of doing so is up to you, and it links to a discussion on the topic for more.
How are tccutil.py releases made and how are they tested?
Every pull request into main must bump util_version in tccutil.py and a required check fails until the version is higher than the one on main and untagged. After merging, the commit is tagged with that version and the tag pushed, and a Release workflow publishes a GitHub release for any vX.Y.Z tag on main whose version matches the script. Tests run with python3 -m unittest discover -s tests -v against TCC.db schemas captured from real Macs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jacobsalmela-tccutil)Was this useful?