├── .gitignore ├── .python-version ├── README.md ├── pyproject.toml ├── src └── python_local │ ├── __init__.py │ └── server.py └── uv.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # Virtual environments 10 | .venv 11 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python_local MCP Server 2 | 3 | An MCP Server that provides an interactive Python REPL (Read-Eval-Print Loop) environment. 4 | 5 | ## Components 6 | 7 | ### Resources 8 | 9 | The server provides access to REPL session history: 10 | - Custom `repl://` URI scheme for accessing session history 11 | - Each session's history can be viewed as a text/plain resource 12 | - History shows input code and corresponding output for each execution 13 | 14 | ### Tools 15 | 16 | The server implements one tool: 17 | - `python_repl`: Executes Python code in a persistent session 18 | - Takes `code` (Python code to execute) and `session_id` as required arguments 19 | - Maintains separate state for each session 20 | - Supports both expressions and statements 21 | - Captures and returns stdout/stderr output 22 | 23 | ## Configuration 24 | 25 | ### Install 26 | 27 | #### Claude Desktop 28 | 29 | On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json` 30 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json` 31 | 32 |
33 | Development/Unpublished Servers Configuration 34 | ```json 35 | "mcpServers": { 36 | "python_local": { 37 | "command": "uv", 38 | "args": [ 39 | "--directory", 40 | "/path/to/python_local", 41 | "run", 42 | "python_local" 43 | ] 44 | } 45 | } 46 | ``` 47 |
48 | 49 |
50 | Published Servers Configuration 51 | ```json 52 | "mcpServers": { 53 | "python_local": { 54 | "command": "uvx", 55 | "args": [ 56 | "python_local" 57 | ] 58 | } 59 | } 60 | ``` 61 |
62 | 63 | ## Development 64 | 65 | ### Building and Publishing 66 | 67 | To prepare the package for distribution: 68 | 69 | 1. Sync dependencies and update lockfile: 70 | ```bash 71 | uv sync 72 | ``` 73 | 74 | 2. Build package distributions: 75 | ```bash 76 | uv build 77 | ``` 78 | 79 | This will create source and wheel distributions in the `dist/` directory. 80 | 81 | 3. Publish to PyPI: 82 | ```bash 83 | uv publish 84 | ``` 85 | 86 | Note: You'll need to set PyPI credentials via environment variables or command flags: 87 | - Token: `--token` or `UV_PUBLISH_TOKEN` 88 | - Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD` 89 | 90 | ### Debugging 91 | 92 | Since MCP servers run over stdio, debugging can be challenging. For the best debugging 93 | experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). 94 | 95 | You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command: 96 | 97 | ```bash 98 | npx @modelcontextprotocol/inspector uv --directory /path/to/python_local run python-local 99 | ``` 100 | 101 | Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging. -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "python-local" 3 | version = "0.1.0" 4 | description = "An MCP Server to run python locally" 5 | readme = "README.md" 6 | requires-python = ">=3.12" 7 | dependencies = [ "mcp>=1.1.0",] 8 | [[project.authors]] 9 | name = "Alex Velikanov" 10 | email = "alex25368@gmail.com" 11 | 12 | [build-system] 13 | requires = [ "hatchling",] 14 | build-backend = "hatchling.build" 15 | 16 | [project.scripts] 17 | python-local = "python_local:main" 18 | -------------------------------------------------------------------------------- /src/python_local/__init__.py: -------------------------------------------------------------------------------- 1 | from . import server 2 | import asyncio 3 | 4 | def main(): 5 | """Main entry point for the package.""" 6 | asyncio.run(server.main()) 7 | 8 | # Optionally expose other important items at package level 9 | __all__ = ['main', 'server'] -------------------------------------------------------------------------------- /src/python_local/server.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import sys 3 | from io import StringIO 4 | import traceback 5 | import builtins 6 | 7 | from mcp.server.models import InitializationOptions 8 | import mcp.types as types 9 | from mcp.server import NotificationOptions, Server 10 | from pydantic import AnyUrl 11 | import mcp.server.stdio 12 | 13 | server = Server("python_interpreter") 14 | 15 | # Global state for REPL sessions 16 | class ReplSession: 17 | def __init__(self): 18 | self.locals = {"__builtins__": builtins} 19 | self.history = [] 20 | 21 | def execute(self, code: str) -> tuple[str, str]: 22 | stdout_capture = StringIO() 23 | stderr_capture = StringIO() 24 | 25 | old_stdout, old_stderr = sys.stdout, sys.stderr 26 | sys.stdout, sys.stderr = stdout_capture, stderr_capture 27 | 28 | try: 29 | # Try to evaluate as expression first 30 | try: 31 | result = eval(code, self.locals) 32 | if result is not None: 33 | print(repr(result)) 34 | except SyntaxError: 35 | # If not an expression, execute as statement 36 | exec(code, self.locals) 37 | 38 | except Exception: 39 | traceback.print_exc() 40 | finally: 41 | sys.stdout, sys.stderr = old_stdout, old_stderr 42 | 43 | return stdout_capture.getvalue(), stderr_capture.getvalue() 44 | 45 | sessions: dict[str, ReplSession] = {} 46 | 47 | @server.list_tools() 48 | async def handle_list_tools() -> list[types.Tool]: 49 | return [ 50 | types.Tool( 51 | name="python_repl", 52 | description="Interactive Python REPL that maintains session state. NOTE THE USER DOES NOT SEE THE STDOUT/STDERR OUTPUT, MAKE SURE TO FORMAT/SUMMARIZEc IT APPROPRIATELY IN YOUR RESPONSE TO THE USER", 53 | inputSchema={ 54 | "type": "object", 55 | "properties": { 56 | "code": {"type": "string", "description": "Python code to execute"}, 57 | "session_id": {"type": "string", "description": "Session identifier"}, 58 | }, 59 | "required": ["code", "session_id"], 60 | }, 61 | ) 62 | ] 63 | 64 | @server.call_tool() 65 | async def handle_call_tool( 66 | name: str, 67 | arguments: dict | None 68 | ) -> list[types.TextContent]: 69 | if name != "python_repl": 70 | raise ValueError(f"Unknown tool: {name}") 71 | 72 | if not arguments or "code" not in arguments or "session_id" not in arguments: 73 | raise ValueError("Missing code or session_id argument") 74 | 75 | code = arguments["code"] 76 | session_id = arguments["session_id"] 77 | 78 | if session_id not in sessions: 79 | sessions[session_id] = ReplSession() 80 | 81 | session = sessions[session_id] 82 | stdout, stderr = session.execute(code) 83 | session.history.append({"code": code, "stdout": stdout, "stderr": stderr}) 84 | 85 | return [types.TextContent(type="text", text=f"NOTE THE USER DOES NOT SEE THIS OUTPUT, MAKE SURE TO FORMAT IT APPROPRIATELY IN YOUR RESPONSE TO THE USER\n\n{stdout}{stderr}")] 86 | 87 | @server.list_resources() 88 | async def handle_list_resources() -> list[types.Resource]: 89 | return [ 90 | types.Resource( 91 | uri=AnyUrl(f"repl://{session_id}/history"), 92 | name=f"REPL Session {session_id}", 93 | description="REPL session history", 94 | mimeType="text/plain", 95 | ) 96 | for session_id in sessions 97 | ] 98 | 99 | @server.read_resource() 100 | async def handle_read_resource(uri: AnyUrl) -> str: 101 | if uri.scheme != "repl": 102 | raise ValueError(f"Unsupported URI scheme: {uri.scheme}") 103 | 104 | session_id = uri.host 105 | if session_id not in sessions: 106 | raise ValueError(f"Session not found: {session_id}") 107 | 108 | history = sessions[session_id].history 109 | return "\n\n".join( 110 | f"In [{i}]: {entry['code']}\n" 111 | f"Out[{i}]:\n{entry['stdout']}{entry['stderr']}" 112 | for i, entry in enumerate(history) 113 | ) 114 | 115 | async def main(): 116 | async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): 117 | await server.run( 118 | read_stream, 119 | write_stream, 120 | InitializationOptions( 121 | server_name="python_interpreter", 122 | server_version="0.1.0", 123 | capabilities=server.get_capabilities( 124 | notification_options=NotificationOptions(), 125 | experimental_capabilities={}, 126 | ), 127 | ), 128 | ) 129 | 130 | if __name__ == "__main__": 131 | asyncio.run(main()) -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.12" 3 | 4 | [[package]] 5 | name = "annotated-types" 6 | version = "0.7.0" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 11 | ] 12 | 13 | [[package]] 14 | name = "anyio" 15 | version = "4.6.2.post1" 16 | source = { registry = "https://pypi.org/simple" } 17 | dependencies = [ 18 | { name = "idna" }, 19 | { name = "sniffio" }, 20 | ] 21 | sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 } 22 | wheels = [ 23 | { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 }, 24 | ] 25 | 26 | [[package]] 27 | name = "certifi" 28 | version = "2024.8.30" 29 | source = { registry = "https://pypi.org/simple" } 30 | sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } 31 | wheels = [ 32 | { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, 33 | ] 34 | 35 | [[package]] 36 | name = "click" 37 | version = "8.1.7" 38 | source = { registry = "https://pypi.org/simple" } 39 | dependencies = [ 40 | { name = "colorama", marker = "platform_system == 'Windows'" }, 41 | ] 42 | sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } 43 | wheels = [ 44 | { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, 45 | ] 46 | 47 | [[package]] 48 | name = "colorama" 49 | version = "0.4.6" 50 | source = { registry = "https://pypi.org/simple" } 51 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 52 | wheels = [ 53 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 54 | ] 55 | 56 | [[package]] 57 | name = "h11" 58 | version = "0.14.0" 59 | source = { registry = "https://pypi.org/simple" } 60 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 61 | wheels = [ 62 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 63 | ] 64 | 65 | [[package]] 66 | name = "httpcore" 67 | version = "1.0.7" 68 | source = { registry = "https://pypi.org/simple" } 69 | dependencies = [ 70 | { name = "certifi" }, 71 | { name = "h11" }, 72 | ] 73 | sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } 74 | wheels = [ 75 | { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, 76 | ] 77 | 78 | [[package]] 79 | name = "httpx" 80 | version = "0.28.0" 81 | source = { registry = "https://pypi.org/simple" } 82 | dependencies = [ 83 | { name = "anyio" }, 84 | { name = "certifi" }, 85 | { name = "httpcore" }, 86 | { name = "idna" }, 87 | ] 88 | sdist = { url = "https://files.pythonhosted.org/packages/10/df/676b7cf674dd1bdc71a64ad393c89879f75e4a0ab8395165b498262ae106/httpx-0.28.0.tar.gz", hash = "sha256:0858d3bab51ba7e386637f22a61d8ccddaeec5f3fe4209da3a6168dbb91573e0", size = 141307 } 89 | wheels = [ 90 | { url = "https://files.pythonhosted.org/packages/8f/fb/a19866137577ba60c6d8b69498dc36be479b13ba454f691348ddf428f185/httpx-0.28.0-py3-none-any.whl", hash = "sha256:dc0b419a0cfeb6e8b34e85167c0da2671206f5095f1baa9663d23bcfd6b535fc", size = 73551 }, 91 | ] 92 | 93 | [[package]] 94 | name = "httpx-sse" 95 | version = "0.4.0" 96 | source = { registry = "https://pypi.org/simple" } 97 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 98 | wheels = [ 99 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 100 | ] 101 | 102 | [[package]] 103 | name = "idna" 104 | version = "3.10" 105 | source = { registry = "https://pypi.org/simple" } 106 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 107 | wheels = [ 108 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 109 | ] 110 | 111 | [[package]] 112 | name = "mcp" 113 | version = "1.1.0" 114 | source = { registry = "https://pypi.org/simple" } 115 | dependencies = [ 116 | { name = "anyio" }, 117 | { name = "httpx" }, 118 | { name = "httpx-sse" }, 119 | { name = "pydantic" }, 120 | { name = "sse-starlette" }, 121 | { name = "starlette" }, 122 | ] 123 | sdist = { url = "https://files.pythonhosted.org/packages/77/f2/067b1fc114e8d3ae4af02fc4f4ed8971a2c4900362d976fabe0f4e9a3418/mcp-1.1.0.tar.gz", hash = "sha256:e3c8d6df93a4de90230ea944dd667730744a3cd91a4cc0ee66a5acd53419e100", size = 83802 } 124 | wheels = [ 125 | { url = "https://files.pythonhosted.org/packages/b9/3e/aef19ac08a6f9a347c086c4e628c2f7329659828cbe92ffd524ec2aac833/mcp-1.1.0-py3-none-any.whl", hash = "sha256:44aa4d2e541f0924d6c344aa7f96b427a6ee1df2fab70b5f9ae2f8777b3f05f2", size = 36576 }, 126 | ] 127 | 128 | [[package]] 129 | name = "pydantic" 130 | version = "2.10.3" 131 | source = { registry = "https://pypi.org/simple" } 132 | dependencies = [ 133 | { name = "annotated-types" }, 134 | { name = "pydantic-core" }, 135 | { name = "typing-extensions" }, 136 | ] 137 | sdist = { url = "https://files.pythonhosted.org/packages/45/0f/27908242621b14e649a84e62b133de45f84c255eecb350ab02979844a788/pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9", size = 786486 } 138 | wheels = [ 139 | { url = "https://files.pythonhosted.org/packages/62/51/72c18c55cf2f46ff4f91ebcc8f75aa30f7305f3d726be3f4ebffb4ae972b/pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", size = 456997 }, 140 | ] 141 | 142 | [[package]] 143 | name = "pydantic-core" 144 | version = "2.27.1" 145 | source = { registry = "https://pypi.org/simple" } 146 | dependencies = [ 147 | { name = "typing-extensions" }, 148 | ] 149 | sdist = { url = "https://files.pythonhosted.org/packages/a6/9f/7de1f19b6aea45aeb441838782d68352e71bfa98ee6fa048d5041991b33e/pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", size = 412785 } 150 | wheels = [ 151 | { url = "https://files.pythonhosted.org/packages/be/51/2e9b3788feb2aebff2aa9dfbf060ec739b38c05c46847601134cc1fed2ea/pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", size = 1895239 }, 152 | { url = "https://files.pythonhosted.org/packages/7b/9e/f8063952e4a7d0127f5d1181addef9377505dcce3be224263b25c4f0bfd9/pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", size = 1805070 }, 153 | { url = "https://files.pythonhosted.org/packages/2c/9d/e1d6c4561d262b52e41b17a7ef8301e2ba80b61e32e94520271029feb5d8/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", size = 1828096 }, 154 | { url = "https://files.pythonhosted.org/packages/be/65/80ff46de4266560baa4332ae3181fffc4488ea7d37282da1a62d10ab89a4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", size = 1857708 }, 155 | { url = "https://files.pythonhosted.org/packages/d5/ca/3370074ad758b04d9562b12ecdb088597f4d9d13893a48a583fb47682cdf/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", size = 2037751 }, 156 | { url = "https://files.pythonhosted.org/packages/b1/e2/4ab72d93367194317b99d051947c071aef6e3eb95f7553eaa4208ecf9ba4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", size = 2733863 }, 157 | { url = "https://files.pythonhosted.org/packages/8a/c6/8ae0831bf77f356bb73127ce5a95fe115b10f820ea480abbd72d3cc7ccf3/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", size = 2161161 }, 158 | { url = "https://files.pythonhosted.org/packages/f1/f4/b2fe73241da2429400fc27ddeaa43e35562f96cf5b67499b2de52b528cad/pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", size = 1993294 }, 159 | { url = "https://files.pythonhosted.org/packages/77/29/4bb008823a7f4cc05828198153f9753b3bd4c104d93b8e0b1bfe4e187540/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", size = 2001468 }, 160 | { url = "https://files.pythonhosted.org/packages/f2/a9/0eaceeba41b9fad851a4107e0cf999a34ae8f0d0d1f829e2574f3d8897b0/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", size = 2091413 }, 161 | { url = "https://files.pythonhosted.org/packages/d8/36/eb8697729725bc610fd73940f0d860d791dc2ad557faaefcbb3edbd2b349/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", size = 2154735 }, 162 | { url = "https://files.pythonhosted.org/packages/52/e5/4f0fbd5c5995cc70d3afed1b5c754055bb67908f55b5cb8000f7112749bf/pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", size = 1833633 }, 163 | { url = "https://files.pythonhosted.org/packages/ee/f2/c61486eee27cae5ac781305658779b4a6b45f9cc9d02c90cb21b940e82cc/pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", size = 1986973 }, 164 | { url = "https://files.pythonhosted.org/packages/df/a6/e3f12ff25f250b02f7c51be89a294689d175ac76e1096c32bf278f29ca1e/pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", size = 1883215 }, 165 | { url = "https://files.pythonhosted.org/packages/0f/d6/91cb99a3c59d7b072bded9959fbeab0a9613d5a4935773c0801f1764c156/pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073", size = 1895033 }, 166 | { url = "https://files.pythonhosted.org/packages/07/42/d35033f81a28b27dedcade9e967e8a40981a765795c9ebae2045bcef05d3/pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08", size = 1807542 }, 167 | { url = "https://files.pythonhosted.org/packages/41/c2/491b59e222ec7e72236e512108ecad532c7f4391a14e971c963f624f7569/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf", size = 1827854 }, 168 | { url = "https://files.pythonhosted.org/packages/e3/f3/363652651779113189cefdbbb619b7b07b7a67ebb6840325117cc8cc3460/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737", size = 1857389 }, 169 | { url = "https://files.pythonhosted.org/packages/5f/97/be804aed6b479af5a945daec7538d8bf358d668bdadde4c7888a2506bdfb/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2", size = 2037934 }, 170 | { url = "https://files.pythonhosted.org/packages/42/01/295f0bd4abf58902917e342ddfe5f76cf66ffabfc57c2e23c7681a1a1197/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107", size = 2735176 }, 171 | { url = "https://files.pythonhosted.org/packages/9d/a0/cd8e9c940ead89cc37812a1a9f310fef59ba2f0b22b4e417d84ab09fa970/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51", size = 2160720 }, 172 | { url = "https://files.pythonhosted.org/packages/73/ae/9d0980e286627e0aeca4c352a60bd760331622c12d576e5ea4441ac7e15e/pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a", size = 1992972 }, 173 | { url = "https://files.pythonhosted.org/packages/bf/ba/ae4480bc0292d54b85cfb954e9d6bd226982949f8316338677d56541b85f/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc", size = 2001477 }, 174 | { url = "https://files.pythonhosted.org/packages/55/b7/e26adf48c2f943092ce54ae14c3c08d0d221ad34ce80b18a50de8ed2cba8/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960", size = 2091186 }, 175 | { url = "https://files.pythonhosted.org/packages/ba/cc/8491fff5b608b3862eb36e7d29d36a1af1c945463ca4c5040bf46cc73f40/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23", size = 2154429 }, 176 | { url = "https://files.pythonhosted.org/packages/78/d8/c080592d80edd3441ab7f88f865f51dae94a157fc64283c680e9f32cf6da/pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05", size = 1833713 }, 177 | { url = "https://files.pythonhosted.org/packages/83/84/5ab82a9ee2538ac95a66e51f6838d6aba6e0a03a42aa185ad2fe404a4e8f/pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337", size = 1987897 }, 178 | { url = "https://files.pythonhosted.org/packages/df/c3/b15fb833926d91d982fde29c0624c9f225da743c7af801dace0d4e187e71/pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", size = 1882983 }, 179 | ] 180 | 181 | [[package]] 182 | name = "python-local" 183 | version = "0.1.0" 184 | source = { editable = "." } 185 | dependencies = [ 186 | { name = "mcp" }, 187 | ] 188 | 189 | [package.metadata] 190 | requires-dist = [{ name = "mcp", specifier = ">=1.1.0" }] 191 | 192 | [[package]] 193 | name = "sniffio" 194 | version = "1.3.1" 195 | source = { registry = "https://pypi.org/simple" } 196 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 197 | wheels = [ 198 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 199 | ] 200 | 201 | [[package]] 202 | name = "sse-starlette" 203 | version = "2.1.3" 204 | source = { registry = "https://pypi.org/simple" } 205 | dependencies = [ 206 | { name = "anyio" }, 207 | { name = "starlette" }, 208 | { name = "uvicorn" }, 209 | ] 210 | sdist = { url = "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz", hash = "sha256:9cd27eb35319e1414e3d2558ee7414487f9529ce3b3cf9b21434fd110e017169", size = 19678 } 211 | wheels = [ 212 | { url = "https://files.pythonhosted.org/packages/52/aa/36b271bc4fa1d2796311ee7c7283a3a1c348bad426d37293609ca4300eef/sse_starlette-2.1.3-py3-none-any.whl", hash = "sha256:8ec846438b4665b9e8c560fcdea6bc8081a3abf7942faa95e5a744999d219772", size = 9383 }, 213 | ] 214 | 215 | [[package]] 216 | name = "starlette" 217 | version = "0.41.3" 218 | source = { registry = "https://pypi.org/simple" } 219 | dependencies = [ 220 | { name = "anyio" }, 221 | ] 222 | sdist = { url = "https://files.pythonhosted.org/packages/1a/4c/9b5764bd22eec91c4039ef4c55334e9187085da2d8a2df7bd570869aae18/starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", size = 2574159 } 223 | wheels = [ 224 | { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, 225 | ] 226 | 227 | [[package]] 228 | name = "typing-extensions" 229 | version = "4.12.2" 230 | source = { registry = "https://pypi.org/simple" } 231 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 232 | wheels = [ 233 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 234 | ] 235 | 236 | [[package]] 237 | name = "uvicorn" 238 | version = "0.32.1" 239 | source = { registry = "https://pypi.org/simple" } 240 | dependencies = [ 241 | { name = "click" }, 242 | { name = "h11" }, 243 | ] 244 | sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725ef307e3d7ddd29b763119b3aa459d02cc05fefcff75/uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175", size = 77630 } 245 | wheels = [ 246 | { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, 247 | ] 248 | --------------------------------------------------------------------------------