onetool-mcp 1.0.0b1__py3-none-any.whl

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.
Files changed (132) hide show
  1. bench/__init__.py +5 -0
  2. bench/cli.py +69 -0
  3. bench/harness/__init__.py +66 -0
  4. bench/harness/client.py +692 -0
  5. bench/harness/config.py +397 -0
  6. bench/harness/csv_writer.py +109 -0
  7. bench/harness/evaluate.py +512 -0
  8. bench/harness/metrics.py +283 -0
  9. bench/harness/runner.py +899 -0
  10. bench/py.typed +0 -0
  11. bench/reporter.py +629 -0
  12. bench/run.py +487 -0
  13. bench/secrets.py +101 -0
  14. bench/utils.py +16 -0
  15. onetool/__init__.py +4 -0
  16. onetool/cli.py +391 -0
  17. onetool/py.typed +0 -0
  18. onetool_mcp-1.0.0b1.dist-info/METADATA +163 -0
  19. onetool_mcp-1.0.0b1.dist-info/RECORD +132 -0
  20. onetool_mcp-1.0.0b1.dist-info/WHEEL +4 -0
  21. onetool_mcp-1.0.0b1.dist-info/entry_points.txt +3 -0
  22. onetool_mcp-1.0.0b1.dist-info/licenses/LICENSE.txt +687 -0
  23. onetool_mcp-1.0.0b1.dist-info/licenses/NOTICE.txt +64 -0
  24. ot/__init__.py +37 -0
  25. ot/__main__.py +6 -0
  26. ot/_cli.py +107 -0
  27. ot/_tui.py +53 -0
  28. ot/config/__init__.py +46 -0
  29. ot/config/defaults/bench.yaml +4 -0
  30. ot/config/defaults/diagram-templates/api-flow.mmd +33 -0
  31. ot/config/defaults/diagram-templates/c4-context.puml +30 -0
  32. ot/config/defaults/diagram-templates/class-diagram.mmd +87 -0
  33. ot/config/defaults/diagram-templates/feature-mindmap.mmd +70 -0
  34. ot/config/defaults/diagram-templates/microservices.d2 +81 -0
  35. ot/config/defaults/diagram-templates/project-gantt.mmd +37 -0
  36. ot/config/defaults/diagram-templates/state-machine.mmd +42 -0
  37. ot/config/defaults/onetool.yaml +25 -0
  38. ot/config/defaults/prompts.yaml +97 -0
  39. ot/config/defaults/servers.yaml +7 -0
  40. ot/config/defaults/snippets.yaml +4 -0
  41. ot/config/defaults/tool_templates/__init__.py +7 -0
  42. ot/config/defaults/tool_templates/extension.py +52 -0
  43. ot/config/defaults/tool_templates/isolated.py +61 -0
  44. ot/config/dynamic.py +121 -0
  45. ot/config/global_templates/__init__.py +2 -0
  46. ot/config/global_templates/bench-secrets-template.yaml +6 -0
  47. ot/config/global_templates/bench.yaml +9 -0
  48. ot/config/global_templates/onetool.yaml +27 -0
  49. ot/config/global_templates/secrets-template.yaml +44 -0
  50. ot/config/global_templates/servers.yaml +18 -0
  51. ot/config/global_templates/snippets.yaml +235 -0
  52. ot/config/loader.py +1087 -0
  53. ot/config/mcp.py +145 -0
  54. ot/config/secrets.py +190 -0
  55. ot/config/tool_config.py +125 -0
  56. ot/decorators.py +116 -0
  57. ot/executor/__init__.py +35 -0
  58. ot/executor/base.py +16 -0
  59. ot/executor/fence_processor.py +83 -0
  60. ot/executor/linter.py +142 -0
  61. ot/executor/pack_proxy.py +260 -0
  62. ot/executor/param_resolver.py +140 -0
  63. ot/executor/pep723.py +288 -0
  64. ot/executor/result_store.py +369 -0
  65. ot/executor/runner.py +496 -0
  66. ot/executor/simple.py +163 -0
  67. ot/executor/tool_loader.py +396 -0
  68. ot/executor/validator.py +398 -0
  69. ot/executor/worker_pool.py +388 -0
  70. ot/executor/worker_proxy.py +189 -0
  71. ot/http_client.py +145 -0
  72. ot/logging/__init__.py +37 -0
  73. ot/logging/config.py +315 -0
  74. ot/logging/entry.py +213 -0
  75. ot/logging/format.py +188 -0
  76. ot/logging/span.py +349 -0
  77. ot/meta.py +1555 -0
  78. ot/paths.py +453 -0
  79. ot/prompts.py +218 -0
  80. ot/proxy/__init__.py +21 -0
  81. ot/proxy/manager.py +396 -0
  82. ot/py.typed +0 -0
  83. ot/registry/__init__.py +189 -0
  84. ot/registry/models.py +57 -0
  85. ot/registry/parser.py +269 -0
  86. ot/registry/registry.py +413 -0
  87. ot/server.py +315 -0
  88. ot/shortcuts/__init__.py +15 -0
  89. ot/shortcuts/aliases.py +87 -0
  90. ot/shortcuts/snippets.py +258 -0
  91. ot/stats/__init__.py +35 -0
  92. ot/stats/html.py +250 -0
  93. ot/stats/jsonl_writer.py +283 -0
  94. ot/stats/reader.py +354 -0
  95. ot/stats/timing.py +57 -0
  96. ot/support.py +63 -0
  97. ot/tools.py +114 -0
  98. ot/utils/__init__.py +81 -0
  99. ot/utils/batch.py +161 -0
  100. ot/utils/cache.py +120 -0
  101. ot/utils/deps.py +403 -0
  102. ot/utils/exceptions.py +23 -0
  103. ot/utils/factory.py +179 -0
  104. ot/utils/format.py +65 -0
  105. ot/utils/http.py +202 -0
  106. ot/utils/platform.py +45 -0
  107. ot/utils/sanitize.py +130 -0
  108. ot/utils/truncate.py +69 -0
  109. ot_tools/__init__.py +4 -0
  110. ot_tools/_convert/__init__.py +12 -0
  111. ot_tools/_convert/excel.py +279 -0
  112. ot_tools/_convert/pdf.py +254 -0
  113. ot_tools/_convert/powerpoint.py +268 -0
  114. ot_tools/_convert/utils.py +358 -0
  115. ot_tools/_convert/word.py +283 -0
  116. ot_tools/brave_search.py +604 -0
  117. ot_tools/code_search.py +736 -0
  118. ot_tools/context7.py +495 -0
  119. ot_tools/convert.py +614 -0
  120. ot_tools/db.py +415 -0
  121. ot_tools/diagram.py +1604 -0
  122. ot_tools/diagram.yaml +167 -0
  123. ot_tools/excel.py +1372 -0
  124. ot_tools/file.py +1348 -0
  125. ot_tools/firecrawl.py +732 -0
  126. ot_tools/grounding_search.py +646 -0
  127. ot_tools/package.py +604 -0
  128. ot_tools/py.typed +0 -0
  129. ot_tools/ripgrep.py +544 -0
  130. ot_tools/scaffold.py +471 -0
  131. ot_tools/transform.py +213 -0
  132. ot_tools/web_fetch.py +384 -0
onetool/cli.py ADDED
@@ -0,0 +1,391 @@
1
+ """Serve CLI entry point for OneTool MCP server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import atexit
6
+ import os
7
+ import signal
8
+ from pathlib import Path
9
+
10
+ import typer
11
+
12
+
13
+ def _suppress_shutdown_warnings() -> None:
14
+ """Suppress pymupdf SWIG warnings at exit.
15
+
16
+ pymupdf emits a DeprecationWarning about swigvarlink during Python's
17
+ interpreter shutdown. This warning is emitted at the C level during
18
+ garbage collection. Redirecting stderr at the fd level suppresses it.
19
+ """
20
+ try:
21
+ # Redirect stderr at the OS level to suppress C-level warnings
22
+ devnull_fd = os.open(os.devnull, os.O_WRONLY)
23
+ os.dup2(devnull_fd, 2)
24
+ os.close(devnull_fd)
25
+ except Exception:
26
+ pass
27
+
28
+
29
+ atexit.register(_suppress_shutdown_warnings)
30
+ from rich.console import Console
31
+
32
+ import ot
33
+ from ot._cli import create_cli, version_callback
34
+ from ot.support import get_support_banner, get_version
35
+
36
+ # Console for CLI output - no auto-highlighting, output to stderr
37
+ console = Console(stderr=True, highlight=False)
38
+
39
+ app = create_cli(
40
+ "onetool",
41
+ "OneTool MCP server - exposes a single 'run' tool for LLM code generation.",
42
+ )
43
+
44
+
45
+ def _print_startup_banner() -> None:
46
+ """Print startup message to stderr."""
47
+ version = get_version()
48
+ console.print(f"[bold cyan]OneTool MCP Server[/bold cyan] [dim]v{version}[/dim]")
49
+ console.print(get_support_banner())
50
+
51
+
52
+ def _setup_signal_handlers() -> None:
53
+ """Set up signal handlers for clean exit."""
54
+
55
+ def handle_signal(signum: int, _frame: object) -> None:
56
+ """Handle termination signals gracefully."""
57
+ sig_name = signal.Signals(signum).name
58
+ console.print(f"\nReceived {sig_name}, shutting down...")
59
+ # Use os._exit() for immediate termination - sys.exit() doesn't work
60
+ # well with asyncio event loops and can require multiple Ctrl+C presses
61
+ os._exit(0)
62
+
63
+ # Handle SIGINT (Ctrl+C) and SIGTERM
64
+ signal.signal(signal.SIGINT, handle_signal)
65
+ signal.signal(signal.SIGTERM, handle_signal)
66
+
67
+ # Init subcommand group - manage global configuration
68
+ init_app = typer.Typer(
69
+ name="init",
70
+ help="Initialize and manage global configuration in ~/.onetool/config/",
71
+ invoke_without_command=True,
72
+ )
73
+ app.add_typer(init_app)
74
+
75
+
76
+ @init_app.callback()
77
+ def init_callback(ctx: typer.Context) -> None:
78
+ """Initialize and manage global configuration in ~/.onetool/config/.
79
+
80
+ Run without subcommand to initialize global config directory.
81
+ """
82
+ if ctx.invoked_subcommand is None:
83
+ # No subcommand = run init
84
+ init_create()
85
+
86
+
87
+ @init_app.command("create", hidden=True)
88
+ def init_create() -> None:
89
+ """Initialize global config directory (~/.onetool/).
90
+
91
+ Creates the global config directory and copies template files if they
92
+ don't already exist. Existing files are preserved.
93
+
94
+ This is the default action when running 'onetool init' without a subcommand.
95
+ """
96
+ from ot.paths import ensure_global_dir, get_global_dir
97
+
98
+ global_dir = get_global_dir()
99
+ if global_dir.exists():
100
+ console.print(f"Global config already exists at {global_dir}/")
101
+ console.print("Use 'onetool init reset' to reinstall templates.")
102
+ return
103
+
104
+ ensure_global_dir(quiet=False, force=False)
105
+
106
+
107
+ @init_app.command("reset")
108
+ def init_reset() -> None:
109
+ """Reset global config to default templates.
110
+
111
+ Prompts for each file before overwriting. For existing files, offers to
112
+ create a backup first. Backups are named file.bak, file.bak.1, etc.
113
+ """
114
+ import shutil
115
+
116
+ from ot.paths import (
117
+ CONFIG_SUBDIR,
118
+ create_backup,
119
+ get_global_dir,
120
+ get_template_files,
121
+ )
122
+
123
+ global_dir = get_global_dir()
124
+ config_dir = global_dir / CONFIG_SUBDIR
125
+
126
+ # Ensure directories exist
127
+ global_dir.mkdir(parents=True, exist_ok=True)
128
+ config_dir.mkdir(exist_ok=True)
129
+
130
+ template_files = get_template_files()
131
+ if not template_files:
132
+ console.print("No template files found.")
133
+ return
134
+
135
+ copied_files: list[str] = []
136
+ backed_up_files: list[tuple[str, Path]] = []
137
+ skipped_files: list[str] = []
138
+
139
+ for source_path, dest_name in template_files:
140
+ dest_path = config_dir / dest_name
141
+ exists = dest_path.exists()
142
+
143
+ if exists:
144
+ # Prompt for overwrite
145
+ console.print(f"\nconfig/{dest_name} already exists.")
146
+ do_overwrite = typer.confirm("Overwrite?", default=True)
147
+
148
+ if not do_overwrite:
149
+ skipped_files.append(dest_name)
150
+ continue
151
+
152
+ # Prompt for backup
153
+ do_backup = typer.confirm(f"Create backup of config/{dest_name}?", default=True)
154
+
155
+ if do_backup:
156
+ backup_path = create_backup(dest_path)
157
+ backed_up_files.append((dest_name, backup_path))
158
+
159
+ shutil.copy(source_path, dest_path)
160
+ copied_files.append(dest_name)
161
+
162
+ # Summary
163
+ console.print()
164
+ if copied_files:
165
+ console.print(f"Reset files in {config_dir}/:")
166
+ for name in copied_files:
167
+ console.print(f" + {name}")
168
+
169
+ if backed_up_files:
170
+ console.print("\nBackups created:")
171
+ for name, backup_path in backed_up_files:
172
+ console.print(f" config/{name} -> {backup_path.name}")
173
+
174
+ if skipped_files:
175
+ console.print("\nSkipped:")
176
+ for name in skipped_files:
177
+ console.print(f" - config/{name}")
178
+
179
+
180
+ @init_app.command("validate")
181
+ def init_validate() -> None:
182
+ """Validate configuration and show status.
183
+
184
+ Checks config files for errors, then displays packs, secrets (names only),
185
+ snippets, aliases, and MCP servers.
186
+ """
187
+ from loguru import logger
188
+
189
+ from ot.config.loader import get_config, load_config
190
+ from ot.config.secrets import load_secrets
191
+ from ot.executor.tool_loader import load_tool_registry
192
+ from ot.paths import CONFIG_SUBDIR, get_global_dir, get_project_dir
193
+
194
+ # Suppress DEBUG logs from config loader
195
+ logger.remove()
196
+
197
+ errors: list[str] = []
198
+ validated: list[str] = []
199
+
200
+ # Check global config
201
+ global_dir = get_global_dir()
202
+ global_config = global_dir / CONFIG_SUBDIR / "onetool.yaml"
203
+ if global_config.exists():
204
+ try:
205
+ load_config(global_config)
206
+ validated.append(str(global_config))
207
+ except Exception as e:
208
+ errors.append(f"{global_config}: {e}")
209
+
210
+ # Check project config
211
+ project_dir = get_project_dir()
212
+ if project_dir:
213
+ project_config = project_dir / CONFIG_SUBDIR / "onetool.yaml"
214
+ if project_config.exists():
215
+ try:
216
+ load_config(project_config)
217
+ validated.append(str(project_config))
218
+ except Exception as e:
219
+ errors.append(f"{project_config}: {e}")
220
+
221
+ # Report validation results
222
+ console.print("Configuration\n")
223
+
224
+ console.print("Directories:")
225
+ global_exists = global_dir.exists()
226
+ if global_exists:
227
+ console.print(f" Global: {global_dir}/ - [green]OK[/green]")
228
+ else:
229
+ console.print(f" Global: {global_dir}/ - [red]missing[/red]")
230
+ if project_dir:
231
+ console.print(f" Project: {project_dir}/ - [green]OK[/green]")
232
+ else:
233
+ console.print(" Project: (none)")
234
+
235
+ if validated:
236
+ console.print("\nConfig files:")
237
+ for path in validated:
238
+ console.print(f" + {path}")
239
+
240
+ if errors:
241
+ console.print("\n[red]Validation errors:[/red]")
242
+ for error in errors:
243
+ console.print(f" ! {error}")
244
+ raise typer.Exit(1)
245
+
246
+ if not validated and not errors:
247
+ console.print("\nNo configuration files found.")
248
+ console.print(f"Checked: {global_config}, .onetool/config/onetool.yaml")
249
+ return
250
+
251
+ # Load merged config for status display
252
+ try:
253
+ config = get_config()
254
+ except Exception as e:
255
+ console.print(f"\n[red]Config error:[/red] {e}")
256
+ return
257
+
258
+ # Packs and tools
259
+ try:
260
+ registry = load_tool_registry()
261
+ if registry.packs:
262
+ total_tools = 0
263
+ pack_list = []
264
+ for pack_name, pack_funcs in sorted(registry.packs.items()):
265
+ from ot.executor.worker_proxy import WorkerPackProxy
266
+
267
+ if isinstance(pack_funcs, WorkerPackProxy):
268
+ func_count = len(pack_funcs.functions)
269
+ else:
270
+ func_count = len(pack_funcs)
271
+ total_tools += func_count
272
+ pack_list.append((pack_name, func_count))
273
+
274
+ console.print(f"\nPacks ({len(pack_list)}, {total_tools} tools):")
275
+ for pack_name, func_count in pack_list:
276
+ console.print(f" {pack_name} ({func_count})")
277
+ else:
278
+ console.print("\nPacks:")
279
+ console.print(" (none)")
280
+ except Exception as e:
281
+ console.print("\nPacks:")
282
+ console.print(f" [red]Error loading tools:[/red] {e}")
283
+
284
+ # Secrets (names only) - use config's secrets_file path
285
+ try:
286
+ secrets_path = config.get_secrets_file_path() if config else None
287
+ secrets = load_secrets(secrets_path)
288
+ if secrets:
289
+ sorted_keys = sorted(secrets.keys())
290
+ console.print(f"\nSecrets ({len(sorted_keys)}):")
291
+ for key in sorted_keys:
292
+ console.print(f" {key} - [green]set[/green]")
293
+ else:
294
+ console.print("\nSecrets:")
295
+ console.print(" (none configured)")
296
+ except Exception as e:
297
+ console.print("\nSecrets:")
298
+ console.print(f" [red]Error:[/red] {e}")
299
+
300
+ # Snippets
301
+ if config and config.snippets:
302
+ sorted_snippets = sorted(config.snippets.keys())
303
+ console.print(f"\nSnippets ({len(sorted_snippets)}):")
304
+ console.print(f" {', '.join(sorted_snippets)}")
305
+ else:
306
+ console.print("\nSnippets:")
307
+ console.print(" (none)")
308
+
309
+ # Aliases
310
+ if config and config.alias:
311
+ sorted_aliases = sorted(config.alias.items())
312
+ console.print(f"\nAliases ({len(sorted_aliases)}):")
313
+ alias_items = [f"{name} -> {target}" for name, target in sorted_aliases]
314
+ console.print(f" {', '.join(alias_items)}")
315
+ else:
316
+ console.print("\nAliases:")
317
+ console.print(" (none)")
318
+
319
+ # Servers
320
+ if config and config.servers:
321
+ sorted_servers = sorted(config.servers.keys())
322
+ console.print(f"\nMCP Servers ({len(sorted_servers)}):")
323
+ console.print(f" {', '.join(sorted_servers)}")
324
+ else:
325
+ console.print("\nMCP Servers:")
326
+ console.print(" (none)")
327
+
328
+
329
+ @app.callback(invoke_without_command=True)
330
+ def serve(
331
+ ctx: typer.Context,
332
+ _version: bool | None = typer.Option(
333
+ None,
334
+ "--version",
335
+ "-v",
336
+ callback=version_callback("onetool", ot.__version__),
337
+ is_eager=True,
338
+ help="Show version and exit.",
339
+ ),
340
+ config: Path | None = typer.Option(
341
+ None,
342
+ "--config",
343
+ "-c",
344
+ help="Path to onetool.yaml configuration file.",
345
+ exists=True,
346
+ readable=True,
347
+ ),
348
+ ) -> None:
349
+ """Run the OneTool MCP server over stdio transport.
350
+
351
+ This starts the MCP server that exposes the 'run' tool for LLM integrations.
352
+ The server communicates via stdio and is typically invoked by MCP clients.
353
+
354
+ Examples:
355
+ onetool
356
+ onetool --config config/onetool.yaml
357
+ """
358
+ # Bootstrap global config directory on first run
359
+ from ot.paths import ensure_global_dir
360
+
361
+ ensure_global_dir(quiet=True)
362
+
363
+ # Only run if no subcommand was invoked (handles --help automatically)
364
+ if ctx.invoked_subcommand is not None:
365
+ return
366
+
367
+ # Load config if specified
368
+ if config:
369
+ from ot.config.loader import get_config
370
+
371
+ get_config(config)
372
+
373
+ # Set up signal handlers for clean exit (before starting server)
374
+ _setup_signal_handlers()
375
+
376
+ # Print startup banner to stderr (stdout is for MCP JSON-RPC)
377
+ _print_startup_banner()
378
+
379
+ # Import here to avoid circular imports and only load when needed
380
+ from ot.server import main as server_main
381
+
382
+ server_main()
383
+
384
+
385
+ def cli() -> None:
386
+ """Run the CLI application."""
387
+ app()
388
+
389
+
390
+ if __name__ == "__main__":
391
+ cli()
onetool/py.typed ADDED
File without changes
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: onetool-mcp
3
+ Version: 1.0.0b1
4
+ Summary: One MCP, unlimited tools.
5
+ Project-URL: Homepage, https://github.com/beycom/onetool
6
+ Project-URL: Repository, https://github.com/beycom/onetool
7
+ Project-URL: Documentation, https://onetool.beycom.online
8
+ Project-URL: Issues, https://github.com/beycom/onetool/issues
9
+ Author-email: Gavin Las <beycom99@gmail.com>
10
+ License: GPL-3.0
11
+ License-File: LICENSE.txt
12
+ License-File: NOTICE.txt
13
+ Keywords: agents,code-execution,context-rot,llm,mcp,mcp-server,model-context-protocol,token-efficiency,tools
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: aiofiles>=25.1.0
24
+ Requires-Dist: docstring-parser>=0.17
25
+ Requires-Dist: duckdb>=1.4.3
26
+ Requires-Dist: fastmcp>=2.14.4
27
+ Requires-Dist: firecrawl>=4.13.4
28
+ Requires-Dist: google-genai>=1.60.0
29
+ Requires-Dist: httpx>=0.28.1
30
+ Requires-Dist: jinja2>=3.1.6
31
+ Requires-Dist: loguru>=0.7.3
32
+ Requires-Dist: mcp>=1.26.0
33
+ Requires-Dist: openai>=2.15.0
34
+ Requires-Dist: openpyxl>=3.1.5
35
+ Requires-Dist: pillow>=12.1.0
36
+ Requires-Dist: pydantic-settings>=2.12.0
37
+ Requires-Dist: pydantic>=2.12.5
38
+ Requires-Dist: pymupdf>=1.26.7
39
+ Requires-Dist: python-docx>=1.2.0
40
+ Requires-Dist: python-pptx>=1.0.2
41
+ Requires-Dist: pyyaml>=6.0.3
42
+ Requires-Dist: questionary>=2.1.1
43
+ Requires-Dist: rich>=14.3.1
44
+ Requires-Dist: sqlalchemy>=2.0.46
45
+ Requires-Dist: trafilatura>=2.0.0
46
+ Requires-Dist: typer>=0.21.1
47
+ Provides-Extra: file
48
+ Requires-Dist: send2trash>=2.1.0; extra == 'file'
49
+ Description-Content-Type: text/markdown
50
+
51
+ <p align="center">
52
+ <img src="docs/assets/onetool-logo.png" alt="OneTool" width="400">
53
+ </p>
54
+
55
+ <p align="center">
56
+ <em>One tool to rule them all, one tool to find them, one tool to bring them all, and in the development bind them.</em>
57
+ </p>
58
+
59
+ <p align="center">
60
+ <a href="https://pypi.org/project/onetool-mcp/"><img alt="PyPI" src="https://img.shields.io/pypi/v/onetool-mcp"></a>
61
+ <a href="https://github.com/beycom/onetool-mcp/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-GPLv3-blue"></a>
62
+ <a href="https://www.python.org/"><img alt="Python" src="https://img.shields.io/badge/python-3.11%2B-blue"></a>
63
+ </p>
64
+
65
+ > **v1.0.0 Pre-Release** - API stable, actively tested.
66
+
67
+ OneTool is a local-first MCP server that exposes a single `run` tool for code execution, giving your AI assistant access to unlimited capabilities through one interface.
68
+
69
+ ## The Problem
70
+
71
+ Connect 5 MCP servers and you've burned 55K tokens before the conversation starts. Connect 10+ and you're at 100K tokens. Your AI gets worse as you add more tools - that's not a bug, it's how context windows work.
72
+
73
+ ## The Solution
74
+
75
+ **98.7% fewer tokens. Same accuracy. 10x lower cost.**
76
+
77
+ Instead of loading 50 separate tool schemas, you write Python directly:
78
+
79
+ ```python
80
+ __ot brave.search(query="AI trends 2026")
81
+ ```
82
+
83
+ No JSON schema parsing. No tool selection loops. No hoping the model guesses correctly. You write explicit code to call APIs - deterministic, visible, no hidden magic.
84
+
85
+ Based on [Anthropic's research](https://www.anthropic.com/engineering/code-execution-with-mcp), which found token usage dropped from 150,000 to 2,000 when presenting tools as code APIs.
86
+
87
+ ## Core Capabilities
88
+
89
+ - **30-second setup** - Install with uv or pip
90
+ - **Drop-in extensibility** - Add a Python file, get a new pack
91
+ - **AST security** - All code validated before execution
92
+ - **Benchmark harness** - Test LLM + MCP combinations with `bench`
93
+
94
+ ## Batteries Included with 100+ Tools
95
+
96
+ See [Tool Reference](docs/tool-reference.md) for the complete list of packs and tools.
97
+
98
+ ## Installation
99
+
100
+ ```bash
101
+ uv tool install onetool-mcp
102
+ ```
103
+
104
+ Or with pip: `pip install onetool-mcp`
105
+
106
+ **With optional dependencies** (for convert, excel, code search):
107
+
108
+ ```bash
109
+ uv tool install onetool-mcp \
110
+ --with pymupdf --with python-docx --with python-pptx \
111
+ --with openpyxl --with Pillow --with duckdb --with openai
112
+ ```
113
+
114
+ Add to Claude Code (`~/.claude/settings.json`):
115
+
116
+ ```json
117
+ {
118
+ "mcpServers": {
119
+ "onetool": {
120
+ "command": "onetool"
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Extending
127
+
128
+ Drop a Python file, get a pack. No registration, no config:
129
+
130
+ ```python
131
+ # tools/mytool.py
132
+ pack = "mytool"
133
+
134
+ def search(*, query: str) -> str:
135
+ """Search for something."""
136
+ return f"Results for: {query}"
137
+ ```
138
+
139
+ ## Why this approach
140
+
141
+ LLMs write Python instead of parsing JSON schemas. You see what's being called. 2K tokens instead of 150K. Adding your own packs is just dropping in a file.
142
+
143
+ ## Documentation
144
+
145
+ - [Why OneTool](docs/intro/index.md) - The problem and our solution
146
+ - [Getting Started](docs/getting-started/quickstart.md) - 2-minute setup
147
+ - [Tools Reference](docs/reference/tools/index.md) - All built-in tools
148
+ - [Extending](docs/extending/index.md) - Create your own tools
149
+
150
+ ## References
151
+
152
+ - [Code Execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp) - Anthropic Engineering
153
+ - [Context Rot](https://research.trychroma.com/context-rot) - Chroma Research
154
+
155
+ ## Licensing
156
+
157
+ **GPLv3** - Will transition to **MIT** at v2.0. Contribute via PRs to help us get there.
158
+
159
+ ## Support
160
+
161
+ If you use or like this project, please consider buying me a coffee:
162
+
163
+ [![Ko-fi](https://img.shields.io/badge/Ko--fi-Buy%20me%20a%20coffee-ff5e5b?logo=ko-fi)](https://ko-fi.com/beycom)
@@ -0,0 +1,132 @@
1
+ bench/__init__.py,sha256=y388vnV0atWMmhJA4RzbIGGHrWNifzidcXkIgjSQR2Q,133
2
+ bench/cli.py,sha256=8QjzxnTkMFvJ-c_bQ0YZkr-MtN9sdZDau_Vo62DFSTQ,1664
3
+ bench/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ bench/reporter.py,sha256=peV3xaDiasQR5UAMCC1oRDuOWG_IAMC6HqQ8XjdXgC8,23602
5
+ bench/run.py,sha256=9s1z_Q4vEE5rdct9aKOpo2A6JLLl8vD0bwQ-jAWB2Ec,16456
6
+ bench/secrets.py,sha256=ho1susoPnJbmdhhMubhdCueQnybMm2ykBapR_nkpTZ0,2676
7
+ bench/utils.py,sha256=Kr3ZHb9cBFaM1HRNBFCUH3JKHk4-fNAiBpr2I95eR0c,386
8
+ bench/harness/__init__.py,sha256=e9qbt4m_7I6j1wlLd_HCI2kQSXNs_vdluFpOLULD25A,1511
9
+ bench/harness/client.py,sha256=qBW1Y49QFEt-NElybRKu1bA1YbuXKv2cPYr58hCSLjw,25303
10
+ bench/harness/config.py,sha256=k6jqvQeumS91ka_QBV8pXtpwQUZhgutbbNiL5n68gxI,12581
11
+ bench/harness/csv_writer.py,sha256=iwy3ixo9KlzoXmJVAYTHYt2Nm8s5vQsxVAaykgsvHEY,3305
12
+ bench/harness/evaluate.py,sha256=Mv6I63aBXwEZHoarUOYu3UIG3-rRcInLtBULlqcqeDk,17262
13
+ bench/harness/metrics.py,sha256=5Zy0x3IG-CeXUk-nxGm8-w2YCds1B2oxNGQMKNkgjGE,9690
14
+ bench/harness/runner.py,sha256=Gx9_IXVBhmogaWCf5e-YNuuyDNeJVJpSPKtbp7PyxaI,38332
15
+ onetool/__init__.py,sha256=NH91OBFDKBASlyorvmrLjckwXblgcToPkdqtZOIya7o,110
16
+ onetool/cli.py,sha256=9TR0BMcUw05O2yt0kcS-3MrCfWPbxavVCtNpsIscxtg,12159
17
+ onetool/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ ot/__init__.py,sha256=WI1BaMpZr1Xag5DLy8710K19v4EEiU0plLsgbCWWgz0,983
19
+ ot/__main__.py,sha256=Lj60MDYULRpq5qUSW2zQF-7eaG7R90v_vB_vl1dv2Fg,123
20
+ ot/_cli.py,sha256=cJ6mazEkl1cseHTUmtPFrsJfjZZ5IFkfFqh0c5s7xuY,2700
21
+ ot/_tui.py,sha256=fbF_iwK58EBPhxWmEEQozneHlSRr7MRop7aRugVQ688,1437
22
+ ot/decorators.py,sha256=8VRje2gT-9C3cpSITKkOSadZF6MBusbqTlRvkafqbAs,3223
23
+ ot/http_client.py,sha256=lwmAISFK9qGwMvWVX4mltZcttt2NUVKaxEnadYzuYy8,4163
24
+ ot/meta.py,sha256=cEaI-3Aul2OcSteY9g7XOheXDswwPo577IqIrFF-qeM,50052
25
+ ot/paths.py,sha256=0R13bHR-8TYBVcMNIepY7O0LXy5xlIZ-1-KRZCJRpJA,14410
26
+ ot/prompts.py,sha256=UdFjOhdhVtgFGJnNWL-I7Mv_KtVBA5Dc9MAG8cwPCOI,6593
27
+ ot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ ot/server.py,sha256=YzVeXrm7wml623CZFTQSFJFyMlL0oD_hKHWN1h_vOJA,9603
29
+ ot/support.py,sha256=u6OQonVDktmQyMxm8GGPSzWmwTuts6a8vNmSjRpuBuU,1639
30
+ ot/tools.py,sha256=MeZx-5sXJ8go7tn_yqs-QYAbhzve-QliL7ZMVO6ilxU,3591
31
+ ot/config/__init__.py,sha256=xa0QbmATFJb01EXZi5kD5j4BgEcfEZZMt2AdKRihxis,1054
32
+ ot/config/dynamic.py,sha256=alaJUnDL2KRIWeCNN-garmPmJ3qFoOirkwh0hSFcMuc,3711
33
+ ot/config/loader.py,sha256=sWjvaaXraQVs45HFGxMrC6P21LPQ2qtbSdBus67JC4U,36016
34
+ ot/config/mcp.py,sha256=iQdDrc3o8UpmW9T-IeMGu_Qyl1lw1lS2vjoTamW4J5k,5072
35
+ ot/config/secrets.py,sha256=zrQZZwRNgnoxyC84WtJgtkcPY7AnB1J22kFDjKI-mxg,6009
36
+ ot/config/tool_config.py,sha256=BisTtEj9Ws8NnWxvSbW9kskkygO8F-RO4Mgm9KIuPm0,3651
37
+ ot/config/defaults/bench.yaml,sha256=roIxVP2QqksP3DS01_DsU87DjyHbQKKjPSVm4h6Lijc,114
38
+ ot/config/defaults/onetool.yaml,sha256=XZRfMsuF91vu9-Z1Ogp51bC5EN3lILtACr6StClirN8,750
39
+ ot/config/defaults/prompts.yaml,sha256=1viP1ELp3uq7U9-xrhlkgNrWWqB5gXtZ-NSOT3zShao,4443
40
+ ot/config/defaults/servers.yaml,sha256=YmrNEhfESEmWTBjK5BTohXw-E1R7SYhJn7RwzRDS-RQ,216
41
+ ot/config/defaults/snippets.yaml,sha256=G7cGnHrX_jUYQqQJi5vPqB4_FaYMdpGDXiFA3ykuK1A,107
42
+ ot/config/defaults/diagram-templates/api-flow.mmd,sha256=YizXla-1o85KyUJqMoT2rOJqFa6wUqhZNf8ZnZtvjyI,696
43
+ ot/config/defaults/diagram-templates/c4-context.puml,sha256=v6KPHcZ2Q_0QY9h80oLTb_xzJUB1Qq5XyeuzNckVh9c,916
44
+ ot/config/defaults/diagram-templates/class-diagram.mmd,sha256=T1VfDg6UK-tKxgT3qZF45JOlMiCdl4Nsa3tmrQhoaLU,1717
45
+ ot/config/defaults/diagram-templates/feature-mindmap.mmd,sha256=mjpih6ehYH5Z8cMquAN7U-8BR8UshC7ggeMOkDQWaF8,1357
46
+ ot/config/defaults/diagram-templates/microservices.d2,sha256=vzW_HJs0-XiOzOjxLZngcqZzjnJGdgPwutqgkXVyaZw,1448
47
+ ot/config/defaults/diagram-templates/project-gantt.mmd,sha256=TNNHDund_OaUM_G1ByGJAPIz95TLBPgtuNMB8rEYu5s,1277
48
+ ot/config/defaults/diagram-templates/state-machine.mmd,sha256=fO36UWr0inb979kyHB1PnLx7RYLzOWd4_ZCBXNmRdDw,971
49
+ ot/config/defaults/tool_templates/__init__.py,sha256=RwRpiQf_iQ5S7uGsfWM92cE8JN_Y1KDqaPfo24h-A5w,214
50
+ ot/config/defaults/tool_templates/extension.py,sha256=VfHxAjiIZ2BdbFMWEkdxGZT4OUxSAUb9ng9p-HH2lCM,1371
51
+ ot/config/defaults/tool_templates/isolated.py,sha256=aTl7_hG1ALHCGqyHMPjKa6fNBXoTBPjsLtbFgq76LAg,1556
52
+ ot/config/global_templates/__init__.py,sha256=aHmytPELRNraAZf2cMf5c9-4yZQUd5m5Z9wg0-dMRKY,109
53
+ ot/config/global_templates/bench-secrets-template.yaml,sha256=srZcnOM8Af2gQTpmo7g16sE9-zj0Tlguv54V-WE3hCs,224
54
+ ot/config/global_templates/bench.yaml,sha256=h2Ac5-unilwx5TPvDsp02y4I0WiPI7y9CyRh7FIBuL8,223
55
+ ot/config/global_templates/onetool.yaml,sha256=hj22T9SkiX5qNao8DisLmV950nAILvUFMX-HoHhVJdI,813
56
+ ot/config/global_templates/secrets-template.yaml,sha256=sUb2uExdrx_07bH2UVKtp-tqCSpcW-PenH_UFDxPF2k,1541
57
+ ot/config/global_templates/servers.yaml,sha256=tDDYHNovK7WuANaH3YepXwhNl9ky35GpAszWKNPmZyw,657
58
+ ot/config/global_templates/snippets.yaml,sha256=r92kjjs21C0gVGsEl7NpnvcjPbmtA1bsPeU8YfoOjs8,9762
59
+ ot/executor/__init__.py,sha256=JGGWInQwvhV1ttr7seATxHbsoL3jwTAUeT0GfeG-56M,836
60
+ ot/executor/base.py,sha256=Z_Sm0Jyf77ffduJG7IS5UCDRX9JquQ6pWm3ek8lEl4o,317
61
+ ot/executor/fence_processor.py,sha256=4GQ0MccQmsuRVSWHRtpv4X1jvL8c2P68gYxjnZl_yj8,2744
62
+ ot/executor/linter.py,sha256=oxXvd-HtWPxHkqUT3d1713xMN5MpxWjuKFQWTLH458U,3704
63
+ ot/executor/pack_proxy.py,sha256=S0d3VsHaXvoraoJdgySAkk4Jc_ExzjGkPbKeCDstUEc,8793
64
+ ot/executor/param_resolver.py,sha256=lQR73bB_aJ1zgXGiI_JmzOiCeUYnW-jiEPHRdquSY00,4361
65
+ ot/executor/pep723.py,sha256=j53wwEtUDWqNe0DMRq0usbySnWEUUeCe4Gd0pMf7qyA,8474
66
+ ot/executor/result_store.py,sha256=BwTd4Gk0JPVmysGB0bC1vtc3M-5q4ZMsfkBle7e-jRE,11104
67
+ ot/executor/runner.py,sha256=Xr-V9k07T773-IN5NJFJmoEH78sj2e3ONsYR5UU4DS0,16432
68
+ ot/executor/simple.py,sha256=Ps181W5FfHOp7CkxF2m5kCe6YZz0qlp4sXSxsysSK6Q,5029
69
+ ot/executor/tool_loader.py,sha256=vdu5pAL1NvGGu1H7tE3lqBafzWHWP3MfFXMZgzgFyIw,13377
70
+ ot/executor/validator.py,sha256=i7xS5EvHVjFFlwX2NKPQJQUK-75gp-XzVGQXs6UCuMY,14241
71
+ ot/executor/worker_pool.py,sha256=HJOu3P9E5MKgBZsddmeElctPUhF5kwx5Hfd9IezwJo8,12635
72
+ ot/executor/worker_proxy.py,sha256=NUxOI2VZFkNbH5IMulvokHEe5cZQtViOSpb4DEHBf4I,5651
73
+ ot/logging/__init__.py,sha256=cEvgp-lB9SNgwIWBoATp0dXroBaaBt4SxYri6AHiBgY,943
74
+ ot/logging/config.py,sha256=PwvQDzcfrcDiUBJhRLyCc07GaMHzn9hu6rFq5wFQCyo,9919
75
+ ot/logging/entry.py,sha256=9kaR2DuxJFUrNUZl6z0IXSjmCmxJS970v8uNykc8L0s,6169
76
+ ot/logging/format.py,sha256=MMpdrvGJqnW2lFOClfhZyzm36rIU7MAq6BUaXOhjFn8,5388
77
+ ot/logging/span.py,sha256=cP2t06AGMWaqFCXRbKyZX0W7gyYyjvIQfFijupLd2ik,11373
78
+ ot/proxy/__init__.py,sha256=N4shk9gsZ76BBe8tUftXL83ELB42zlbTXtF05lTUqUc,435
79
+ ot/proxy/manager.py,sha256=ZhivveB9MthYv5oeI_vj61uYxkN82wMAPzPOdjTaGkg,13442
80
+ ot/registry/__init__.py,sha256=VAvn1JAL8dZrsTh_Y7NilIrLo9blbhWt38TN-FcTapM,5149
81
+ ot/registry/models.py,sha256=Z-lsRRN3pDyL2nUalZA9QoD1IG_e7aSPUoE6TvRspCk,2160
82
+ ot/registry/parser.py,sha256=NXLFXYIMjFfEb4ztTLZqz4GxYZw-uEWAdxDuMMT_iVI,8416
83
+ ot/registry/registry.py,sha256=HFPPIGWop2szXRQeQeFs5axK4iS17dEOHV8qf9Jm5nE,13458
84
+ ot/shortcuts/__init__.py,sha256=n-yQvYHoEIg-7Ydm_P7bA86AJk5iq9o38pLiAj_zGGY,461
85
+ ot/shortcuts/aliases.py,sha256=0oCgkvTQcPS2TqNYa6uGwLYunA8-1L-FfrHCVynZ4-Q,2705
86
+ ot/shortcuts/snippets.py,sha256=rCbyFdQ4_1HY4AhxeLRMUfDXDcVN3FZpEUp_8ACRIiY,7956
87
+ ot/stats/__init__.py,sha256=5IP7bkfeXZm_B-WS9kNnQDCoggwF0Mit3dFtBe5CvKU,933
88
+ ot/stats/html.py,sha256=vlMCxfyjm6gipNyPYP5O8Nicmhq8tsP0l6Ifpc1-dnY,9079
89
+ ot/stats/jsonl_writer.py,sha256=ym-Wfy0gv90M59ipCQsxapTKNQBO28btKDe6kgFdxck,8169
90
+ ot/stats/reader.py,sha256=zKNegWFSbEBekBDmbgzVTv1Lj3UckRZKkiGkfqPN3XY,11883
91
+ ot/stats/timing.py,sha256=ZAzigZwmAdyRKdUBjtkIuAi_KJXyrsygwSxm0_NM7aE,1500
92
+ ot/utils/__init__.py,sha256=k54kcSGmKl8zrnKXw9svCyXej7FrVZvshMSK1f_6de4,2108
93
+ ot/utils/batch.py,sha256=0oi0wx2kn0XlbB03Utn9lJho_Fmdomasl1_F5AauSdU,4939
94
+ ot/utils/cache.py,sha256=s3z7R_xv-hWxB_q76L7eFPbwnc_KFYRoZllnRloQswg,3506
95
+ ot/utils/deps.py,sha256=5U_bnAEfd_Ivt9ZZitn9emt8T7Ehm99_xb1_okbURfA,11654
96
+ ot/utils/exceptions.py,sha256=_kYtWpnxkBPF2TXkXksPeX2MfJLrCJ2VU-1CckPjZoU,613
97
+ ot/utils/factory.py,sha256=UhKV6Wa2FOZHh4SKWmfeps1ThboS0rQnBUL38-eMljE,5106
98
+ ot/utils/format.py,sha256=4bUvYnsUD9Wz2czPsSoxQOIqxrFIlx-n3Y5HdtZpfxc,2065
99
+ ot/utils/http.py,sha256=IaRTxn--2TJYGpAquywsuHm_xJQxOcMvtFDfuaJUJIU,5653
100
+ ot/utils/platform.py,sha256=dBnxrLN-H2W0onc_LM9BML5WXmKahdpdkW2pVdauup4,1512
101
+ ot/utils/sanitize.py,sha256=5zDKpDwE6oo-74Bwv-yhovOJ6zk03vO2-gsCiEDA2kk,3815
102
+ ot/utils/truncate.py,sha256=03ObrMxElh3CM5lhwThA00oLgPzzFSOPR9Y8BmzAwis,1771
103
+ ot_tools/__init__.py,sha256=uUxp9V0E1lqzeT3wOx10JHzAFu3esn7M6FJrwBn_8Z0,95
104
+ ot_tools/brave_search.py,sha256=JPVR_S2J1NLL7ad4AudIkgMsvnqggibjZ5FRXPt5ZA0,17746
105
+ ot_tools/code_search.py,sha256=P3-oReY7n_3nrlEiYzFv5ompcU-cuVRTeEK4fSQKuew,25191
106
+ ot_tools/context7.py,sha256=sn75QMEeOeSQSYZeVwT4UjYEXo3QXa8KC61mwIUix3U,16088
107
+ ot_tools/convert.py,sha256=mCmDFLWgLiEJhYyd6Ex9s7qfyPOWj7FKaqtIFKG_Ig4,20488
108
+ ot_tools/db.py,sha256=RH_OOUJYqYVw14O8QYGB9QKg9IGNPQAM33HDa71D3v4,13142
109
+ ot_tools/diagram.py,sha256=tGNyd3PYdpi3Qs7Gs8pbcZVWMBVmyNbJG_wxiZ8682M,50478
110
+ ot_tools/diagram.yaml,sha256=Q4twP0rH-1zPWFbgBhl_RZbUg5dkmldX5E6Lp49Ugp4,5439
111
+ ot_tools/excel.py,sha256=yZGgKzDzPvZAE7b9vjC25FY7lJkw7PCBFMYOGcU1UyE,43067
112
+ ot_tools/file.py,sha256=9o_A1m7GCxpnIKIwAiahvGaQrfxXcfkVQicPH1_ztW0,46689
113
+ ot_tools/firecrawl.py,sha256=PtWtiKSUGl0VUdZfMhfHOmQSJ__UN2xcJ-BBuVaXmgo,23433
114
+ ot_tools/grounding_search.py,sha256=ak0wZzBQMPR5VYVrATOIzldZj_n8UxYeCxrmpTZrykA,20182
115
+ ot_tools/package.py,sha256=5MtsPW9ieolQAnaw60Yb4TyODSlowW56HAZj-j_QSkU,19148
116
+ ot_tools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
+ ot_tools/ripgrep.py,sha256=_dKHo5TTlYlzoRQV8-7kFw3yuYk7I5r_9FYJvnGFECc,17105
118
+ ot_tools/scaffold.py,sha256=92pjn0kqncK-2XrzEUn058HRkp1iJpAvWInaCKVdbGI,17374
119
+ ot_tools/transform.py,sha256=IQ6bBZ4mDS7c-8FzpUSQB1EwseYgq3kVGr5LFxLBcwA,6747
120
+ ot_tools/web_fetch.py,sha256=l9taGigHIW8QMaFRObjQuoUNdv9MzYMIyZgtdaTO3DA,13483
121
+ ot_tools/_convert/__init__.py,sha256=Cwo87GiP8JUWpGFSpbkCEyt_q4PUKauFpbqzyOIFOy0,465
122
+ ot_tools/_convert/excel.py,sha256=mVCgolocTKd7ZCpVd2hANaYnhJrneNtGYEZafrCeYqE,8679
123
+ ot_tools/_convert/pdf.py,sha256=6ATY_8NGrc5877Hhw-z5Gxo4Nxsw7MBhVLaHwAmvBXE,7323
124
+ ot_tools/_convert/powerpoint.py,sha256=M8lwkTV7x4wafR7eXrVojCBKgcXVxO1tT-Z2Ml5wwxo,7855
125
+ ot_tools/_convert/utils.py,sha256=AfId8ulTx_kKq0zVbhvuY3KGXqQjEk8Y7o8BXjP8WgA,10192
126
+ ot_tools/_convert/word.py,sha256=XfjqQRhEYS_433LprmF6vqyosytXVdNWzsCXo42WNcA,8710
127
+ onetool_mcp-1.0.0b1.dist-info/METADATA,sha256=S4eE3OnUcKTZpd8MK0COy_vlI9kzMV1ZrCatCbQ1E0w,5584
128
+ onetool_mcp-1.0.0b1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
129
+ onetool_mcp-1.0.0b1.dist-info/entry_points.txt,sha256=UUr3FvhGxnRdyJSxRG5YeqCGUi8nQ3oMCoueM35sF04,66
130
+ onetool_mcp-1.0.0b1.dist-info/licenses/LICENSE.txt,sha256=EAAF-2Agl1ewXnFl4KFpREq8Jh1YFRiogyBIzfGyImM,35561
131
+ onetool_mcp-1.0.0b1.dist-info/licenses/NOTICE.txt,sha256=GTNDiQ3_2O_AcngYY3t41ZQ7S_E7r0EcflmdZGjGFxM,2470
132
+ onetool_mcp-1.0.0b1.dist-info/RECORD,,