codex-mcp-eraser 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-mcp-eraser
3
+ Version: 0.0.1
4
+ Summary: Safely remove user-configured MCP servers and hooks from Codex CLI
5
+ Classifier: Environment :: Console
6
+ Classifier: Operating System :: OS Independent
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+
15
+ # codex-mcp-eraser
16
+
17
+ A Python command-line tool that removes user-configured MCP server tables and
18
+ hooks from Codex CLI configuration. It does not modify plugin settings or
19
+ managed policies.
20
+
21
+ Requires Python 3.11 or later.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ python -m pip install codex-mcp-eraser
27
+ ```
28
+
29
+ ## Use
30
+
31
+ The default is a dry run. Review the paths and changes before applying them:
32
+
33
+ ```bash
34
+ codex-mcp-eraser
35
+ codex-mcp-eraser --apply
36
+ ```
37
+
38
+ Use `--yes` with `--apply` to skip the confirmation prompt. `--project PATH`
39
+ also checks that project's `.codex/` directory. `--codex-home PATH` overrides
40
+ the user configuration directory; otherwise `CODEX_HOME` or `~/.codex` is used.
41
+
42
+ Before changing or removing files, the tool creates a permission-restricted
43
+ backup under the Codex home directory.
@@ -0,0 +1,29 @@
1
+ # codex-mcp-eraser
2
+
3
+ A Python command-line tool that removes user-configured MCP server tables and
4
+ hooks from Codex CLI configuration. It does not modify plugin settings or
5
+ managed policies.
6
+
7
+ Requires Python 3.11 or later.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ python -m pip install codex-mcp-eraser
13
+ ```
14
+
15
+ ## Use
16
+
17
+ The default is a dry run. Review the paths and changes before applying them:
18
+
19
+ ```bash
20
+ codex-mcp-eraser
21
+ codex-mcp-eraser --apply
22
+ ```
23
+
24
+ Use `--yes` with `--apply` to skip the confirmation prompt. `--project PATH`
25
+ also checks that project's `.codex/` directory. `--codex-home PATH` overrides
26
+ the user configuration directory; otherwise `CODEX_HOME` or `~/.codex` is used.
27
+
28
+ Before changing or removing files, the tool creates a permission-restricted
29
+ backup under the Codex home directory.
@@ -0,0 +1,3 @@
1
+ """Remove user-configured MCP servers and hooks from Codex CLI."""
2
+
3
+ __version__ = '0.0.1'
@@ -0,0 +1,8 @@
1
+ """Run codex-mcp-eraser as a module."""
2
+ import sys
3
+
4
+ from .cli import main
5
+
6
+
7
+ if __name__ == '__main__':
8
+ sys.exit(main())
@@ -0,0 +1,153 @@
1
+ """Command-line interface for safely removing Codex MCP servers and hooks."""
2
+ import argparse
3
+ import datetime as dt
4
+ import os
5
+ from pathlib import Path
6
+ import re
7
+ import shutil
8
+ import sys
9
+ import tempfile
10
+ import tomllib
11
+
12
+
13
+ HEADER = re.compile(r'^\s*\[\[?\s*(.*?)\s*\]\]?\s*(?:#.*)?$')
14
+
15
+
16
+ def toml_path(header):
17
+ """Divide TOML table paths while respecting quoted segments."""
18
+ result, part, quote, escape = [], '', None, False
19
+ for char in header:
20
+ if escape:
21
+ part += char
22
+ escape = False
23
+ elif quote == '"' and char == '\\':
24
+ part += char
25
+ escape = True
26
+ elif quote:
27
+ if char == quote:
28
+ quote = None
29
+ else:
30
+ part += char
31
+ elif char in ('"', "'"):
32
+ quote = char
33
+ elif char == '.':
34
+ result.append(part.strip())
35
+ part = ''
36
+ else:
37
+ part += char
38
+ if quote:
39
+ raise ValueError('Cabecera TOML con comillas incompletas')
40
+ result.append(part.strip())
41
+ return result
42
+
43
+
44
+ def transform(content):
45
+ original = tomllib.loads(content)
46
+ lines = content.splitlines(keepends=True)
47
+ keep, section, removed = [], (), set()
48
+ # Table headers are the only safe way to selectively edit these settings.
49
+ for line in lines:
50
+ match = HEADER.match(line.rstrip('\r\n'))
51
+ if match:
52
+ section = tuple(toml_path(match.group(1)))
53
+ drop = bool(section and section[0] in ('mcp_servers', 'hooks'))
54
+ if drop:
55
+ removed.add(section[0])
56
+ else:
57
+ keep.append(line)
58
+ result = ''.join(keep)
59
+ updated = tomllib.loads(result)
60
+ # Stop without changes if an unsupported representation remains.
61
+ if 'mcp_servers' in updated or 'hooks' in updated:
62
+ raise ValueError('Hay MCP/hooks fuera de tablas TOML editables; se requiere revisión manual')
63
+ if any(key in original for key in ('mcp_servers', 'hooks')) and not removed:
64
+ raise ValueError('No se localizaron tablas de MCP/hooks para eliminar con seguridad')
65
+ return result, removed
66
+
67
+
68
+ def atomic_write(path, content):
69
+ stat = path.stat()
70
+ with tempfile.NamedTemporaryFile('w', encoding='utf-8', dir=path.parent,
71
+ prefix='.codex-reset-', delete=False) as tmp:
72
+ tmp.write(content)
73
+ tmp_path = Path(tmp.name)
74
+ try:
75
+ os.chmod(tmp_path, stat.st_mode & 0o777)
76
+ os.replace(tmp_path, path)
77
+ finally:
78
+ if tmp_path.exists():
79
+ tmp_path.unlink()
80
+
81
+
82
+ def _run():
83
+ parser = argparse.ArgumentParser(description=__doc__)
84
+ parser.add_argument('--dry-run', action='store_true', help='Solo muestra los cambios')
85
+ parser.add_argument('--apply', action='store_true', help='Aplica cambios tras confirmar')
86
+ parser.add_argument('--yes', action='store_true', help='Omite la confirmación (requiere --apply)')
87
+ parser.add_argument('--project', type=Path, help='Incluye .codex/ del proyecto indicado')
88
+ parser.add_argument('--codex-home', type=Path,
89
+ default=Path(os.environ.get('CODEX_HOME', str(Path.home() / '.codex'))),
90
+ help='Directorio de configuración del usuario de Codex')
91
+ args = parser.parse_args()
92
+ if args.apply and args.dry_run:
93
+ parser.error('--apply y --dry-run son incompatibles')
94
+ if args.yes and not args.apply:
95
+ parser.error('--yes requiere --apply')
96
+
97
+ dirs = [args.codex_home.expanduser().resolve()]
98
+ if args.project:
99
+ dirs.append((args.project.expanduser().resolve() / '.codex'))
100
+ targets = []
101
+ print('Modo:', 'APLICAR' if args.apply else 'DRY-RUN (sin cambios)')
102
+ for directory in dict.fromkeys(dirs):
103
+ config = directory / 'config.toml'
104
+ hooks = directory / 'hooks.json'
105
+ if config.is_symlink() or hooks.is_symlink():
106
+ raise RuntimeError(f'Enlaces simbólicos no admitidos: {directory}')
107
+ if config.is_file():
108
+ content = config.read_text(encoding='utf-8')
109
+ changed, sections = transform(content)
110
+ if changed != content:
111
+ targets.append((config, changed))
112
+ print(f' EDITAR {config}: quitar tablas {", ".join(sorted(sections))}')
113
+ if hooks.is_file():
114
+ targets.append((hooks, None))
115
+ print(f' RETIRAR {hooks}: hooks de usuario/proyecto')
116
+ if not targets:
117
+ print('No hay cambios en las ubicaciones revisadas.')
118
+ return 0
119
+ if not args.apply:
120
+ print('Para ejecutar: codex-mcp-eraser --apply')
121
+ return 0
122
+ if not args.yes:
123
+ answer = input('Escribe RESTABLECER para continuar: ')
124
+ if answer != 'RESTABLECER':
125
+ print('Cancelado, sin cambios.')
126
+ return 1
127
+ stamp = dt.datetime.now().strftime('%Y%m%d-%H%M%S-%f')
128
+ # Keep backups under Codex home, away from external credentials.
129
+ backup = dirs[0] / 'backups' / f'mcp-hooks-reset-{stamp}'
130
+ backup.mkdir(parents=True, mode=0o700, exist_ok=False)
131
+ os.chmod(backup, 0o700)
132
+ for i, (path, _) in enumerate(targets):
133
+ dst = backup / f'{i:02d}-{path.name}'
134
+ shutil.copy2(path, dst)
135
+ os.chmod(dst, 0o600)
136
+ print(f' RESPALDO {path} -> {dst}')
137
+ for path, content in targets:
138
+ if content is None:
139
+ path.unlink()
140
+ else:
141
+ atomic_write(path, content)
142
+ print(f'Listo. Copia de seguridad: {backup}')
143
+ print('Reinicia Codex. Los hooks/MCP de plugins o configuración administrada no se modificaron.')
144
+ return 0
145
+
146
+
147
+ def main():
148
+ """Run the CLI and convert operational errors into a concise exit status."""
149
+ try:
150
+ return _run()
151
+ except (OSError, ValueError, RuntimeError, tomllib.TOMLDecodeError) as exc:
152
+ print(f'ERROR: {exc}', file=sys.stderr)
153
+ return 2
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-mcp-eraser
3
+ Version: 0.0.1
4
+ Summary: Safely remove user-configured MCP servers and hooks from Codex CLI
5
+ Classifier: Environment :: Console
6
+ Classifier: Operating System :: OS Independent
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+
15
+ # codex-mcp-eraser
16
+
17
+ A Python command-line tool that removes user-configured MCP server tables and
18
+ hooks from Codex CLI configuration. It does not modify plugin settings or
19
+ managed policies.
20
+
21
+ Requires Python 3.11 or later.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ python -m pip install codex-mcp-eraser
27
+ ```
28
+
29
+ ## Use
30
+
31
+ The default is a dry run. Review the paths and changes before applying them:
32
+
33
+ ```bash
34
+ codex-mcp-eraser
35
+ codex-mcp-eraser --apply
36
+ ```
37
+
38
+ Use `--yes` with `--apply` to skip the confirmation prompt. `--project PATH`
39
+ also checks that project's `.codex/` directory. `--codex-home PATH` overrides
40
+ the user configuration directory; otherwise `CODEX_HOME` or `~/.codex` is used.
41
+
42
+ Before changing or removing files, the tool creates a permission-restricted
43
+ backup under the Codex home directory.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ codex_mcp_eraser/__init__.py
4
+ codex_mcp_eraser/__main__.py
5
+ codex_mcp_eraser/cli.py
6
+ codex_mcp_eraser.egg-info/PKG-INFO
7
+ codex_mcp_eraser.egg-info/SOURCES.txt
8
+ codex_mcp_eraser.egg-info/dependency_links.txt
9
+ codex_mcp_eraser.egg-info/entry_points.txt
10
+ codex_mcp_eraser.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ codex-mcp-eraser = codex_mcp_eraser.cli:main
@@ -0,0 +1 @@
1
+ codex_mcp_eraser
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "codex-mcp-eraser"
7
+ version = "0.0.1"
8
+ description = "Safely remove user-configured MCP servers and hooks from Codex CLI"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ classifiers = [
12
+ "Environment :: Console",
13
+ "Operating System :: OS Independent",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ ]
20
+
21
+ [project.scripts]
22
+ codex-mcp-eraser = "codex_mcp_eraser.cli:main"
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["."]
26
+ include = ["codex_mcp_eraser*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+