[project] name = "kitchensink4web" version = "1.0.2" description = "Browser tools for your AI assistant: budgeted page reads, tables, screenshots and optional clicking and typing. Read-only by default, runs locally, for Claude Code, Codex CLI and other MCP clients." readme = "README.md" # DESIGN 10.3 rule 5 held the license out of every file until the decision # landed and named the LICENSE file a Phase 9 artifact. The decision landed # 2026-09-06 (Q1: the family AGPL), so this is Phase 9 delivering it. LICENSE # is the canonical AGPL-3.0 text from gnu.org, byte-exact and unmodified, so # that GitHub's license detection identifies it; it is byte-identical to the # sibling repos' LICENSE for the same reason. NOTICE.md carries the product # copyright line and the retargeted per-product notice, the same split the # siblings use. license = "AGPL-3.0-only" license-files = ["LICENSE", "NOTICE.md"] authors = [{ name = "Alvut Consulting, LLC (KitchenSink4AI)" }] keywords = [ "mcp", "browser", "playwright", "web", "automation", "claude", "model-context-protocol", "token-efficient", "accessibility-tree", "read-only", "audit-trail", "scraping", "forms", "downloads", ] classifiers = [ "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Libraries :: Python Modules", ] requires-python = ">=3.12" dependencies = [ # fastmcp is PINNED from day one (the Word v2 lesson: never ship # unpinned fastmcp; a minor bump moved the visibility API mid-build). "fastmcp>=3.4,<4", # The caller-pattern ReDoS guard (policy/_regex.py). find_elements takes # user regex against page text, so this matters more here than in the # document family. "regex", # THE ENGINE (DESIGN 4.1), required since Phase 1 opened. The wheel is # 38.2 MB because it bundles a Node runtime and the playwright-core # driver; the BROWSERS are not bundled and are downloaded on first use, # per engine, so an install of KS4Web costs the wheel and nothing else # until somebody asks it to browse. "playwright>=1.62", # The budget meter's estimator. DESIGN 3.4 fixes the convention at # tiktoken on o200k_base and names it in every published number, which is # what makes "a page view never exceeds its budget" checkable rather than # a slogan: conflict record #4 has the same content measured four ways at # 4,024, 4,637, 11,717, and 14,400 tokens. "tiktoken", ] [project.optional-dependencies] dev = [ "pytest", "pytest-timeout", ] # Reading text out of PIXELS, where the DOM has none: a canvas, an error # rendered as an image, a screenshot embedded in a page. The engine is the # one already inside Windows, reached through the PyWinRT projection # packages: MIT, wheels only, no native install and no subprocess. Optional # and Windows-only by nature, so `read_image_text` refuses with the reason # named everywhere else rather than degrading in silence. ocr = [ "winrt-runtime==3.2.1; sys_platform == 'win32'", "winrt-Windows.Media.Ocr==3.2.1; sys_platform == 'win32'", "winrt-Windows.Graphics.Imaging==3.2.1; sys_platform == 'win32'", "winrt-Windows.Storage.Streams==3.2.1; sys_platform == 'win32'", "winrt-Windows.Globalization==3.2.1; sys_platform == 'win32'", # THE ASYNC PLUMBING, and it is not optional (found by the seven-branch # integration, 2026-09-08). PyWinRT awaits an IAsyncOperation by setting # `op.completed`, which imports `winrt.windows.foundation` at that moment. # Without these two the extra installs, `probe()` answers "available" # because the OCR engine really is there, and then every call HANGS until # the module's own wall clock expires: the completion callback raises # ModuleNotFoundError inside the runtime's waiter and the awaitable never # resolves. A hang is the one failure mode this tool exists not to have, # so the dependency that makes the await complete is pinned beside the # engine that does the reading. "winrt-Windows.Foundation==3.2.1; sys_platform == 'win32'", "winrt-Windows.Foundation.Collections==3.2.1; sys_platform == 'win32'", ] # The accessibility engine, as a PINNED DEPENDENCY rather than a vendored # file (author ruling 2026-09-07). axe-core arrives inside this MIT wrapper # from its own publisher, under its own name, with its own license files; # this project redistributes none of it. Pinned exactly, because a vendored # asset has no resolver to argue with and a bumped engine changes what a # result MEANS. `get_accessibility` reports the version it actually ran. accessibility = ["axe-playwright-python==0.1.8"] [project.urls] Homepage = "https://kitchensink4.ai" Documentation = "https://github.com/KitchenSink4AI/KitchenSink4Web#readme" Repository = "https://github.com/KitchenSink4AI/KitchenSink4Web" Issues = "https://github.com/KitchenSink4AI/KitchenSink4Web/issues" [project.scripts] # Q11, RULED by the author 2026-09-04. web-mcp = "kitchensink4web.server:main" kitchensink4web = "kitchensink4web.server:main" [build-system] requires = ["setuptools>=77"] build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] where = ["src"] [tool.setuptools.package-data] # The in-page extractor is the projection, and a wheel without it installs a # server that cannot read a page. Declared here rather than discovered at # ship time. # `figures.json` is the measured evidence figure get_server_info reports. # It ships INSIDE the wheel because the question it answers ("how much is # this thing tested") is asked of a running server, which has no test suite # to count and must not invent a number. kitchensink4web = ["projection/*.js", "profile_data/*.json", "figures.json"] [tool.pytest.ini_options] testpaths = ["tests"] timeout = 120 markers = [ "browser: needs a real browser (none exist before Phase 1; auto-skipped)", ]