pythonparts-mcp 0.2.0__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.
- pythonparts_mcp-0.2.0/.gitignore +47 -0
- pythonparts_mcp-0.2.0/CHANGELOG.md +56 -0
- pythonparts_mcp-0.2.0/DOCS.md +172 -0
- pythonparts_mcp-0.2.0/PKG-INFO +179 -0
- pythonparts_mcp-0.2.0/PUBLISH.md +91 -0
- pythonparts_mcp-0.2.0/README.md +169 -0
- pythonparts_mcp-0.2.0/pyproject.toml +27 -0
- pythonparts_mcp-0.2.0/server/__init__.py +0 -0
- pythonparts_mcp-0.2.0/server/__main__.py +101 -0
- pythonparts_mcp-0.2.0/server/config.py +58 -0
- pythonparts_mcp-0.2.0/server/paths.py +87 -0
- pythonparts_mcp-0.2.0/server/pyright_config.py +67 -0
- pythonparts_mcp-0.2.0/server/registry.py +59 -0
- pythonparts_mcp-0.2.0/server/tools/__init__.py +23 -0
- pythonparts_mcp-0.2.0/server/tools/_introspect_worker.py +537 -0
- pythonparts_mcp-0.2.0/server/tools/_sandbox_worker.py +196 -0
- pythonparts_mcp-0.2.0/server/tools/connector.py +81 -0
- pythonparts_mcp-0.2.0/server/tools/inspect_symbol.py +75 -0
- pythonparts_mcp-0.2.0/server/tools/run_snippet_in_allplan.py +85 -0
- pythonparts_mcp-0.2.0/server/tools/run_snippet_in_sandbox.py +91 -0
- pythonparts_mcp-0.2.0/server/tools/runtime_context.py +157 -0
- pythonparts_mcp-0.2.0/server/tools/typecheck.py +140 -0
- pythonparts_mcp-0.2.0/server/worker.py +184 -0
- pythonparts_mcp-0.2.0/tests/__init__.py +1 -0
- pythonparts_mcp-0.2.0/tests/test_tools.py +320 -0
- pythonparts_mcp-0.2.0/worker_inspect_tool_check.cmd +9 -0
- pythonparts_mcp-0.2.0/worker_inspect_tool_kill.cmd +16 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# Environment variables
|
|
19
|
+
.env
|
|
20
|
+
|
|
21
|
+
# Pyright / type checking
|
|
22
|
+
.pyright/
|
|
23
|
+
|
|
24
|
+
# pytest
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
htmlcov/
|
|
27
|
+
.coverage
|
|
28
|
+
|
|
29
|
+
# mypy
|
|
30
|
+
.mypy_cache/
|
|
31
|
+
|
|
32
|
+
# IDEs
|
|
33
|
+
.vscode/settings.json
|
|
34
|
+
.idea/
|
|
35
|
+
|
|
36
|
+
# OS
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
|
|
40
|
+
# Logs
|
|
41
|
+
*.log
|
|
42
|
+
|
|
43
|
+
# Visual Studio Code
|
|
44
|
+
.vscode/
|
|
45
|
+
|
|
46
|
+
# Other
|
|
47
|
+
.temp/
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `pythonparts-mcp` are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] — 2026-09-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`run_snippet_in_sandbox`** — runs a Python snippet against the ALLPLAN libraries and
|
|
15
|
+
returns its `print()` output, so an agent can check geometry math or API behaviour instead
|
|
16
|
+
of guessing at it.
|
|
17
|
+
|
|
18
|
+
The snippet runs in a separate process under ALLPLAN's own interpreter, which means
|
|
19
|
+
ALLPLAN does not need to be running and the snippet cannot reach the user's drawing or
|
|
20
|
+
project data. The ALLPLAN interface modules are pre-imported as globals; `document` and
|
|
21
|
+
`coord_input` do not exist, and calls that open a user interface will hang until the
|
|
22
|
+
60-second timeout. Each call gets a fresh set of globals, so snippets must be
|
|
23
|
+
self-contained.
|
|
24
|
+
|
|
25
|
+
The worker process is started on first use and reused, so only the first call pays for
|
|
26
|
+
loading the ALLPLAN DLLs.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `inspect_symbol` declared a 30-second timeout but never applied it, so a wedged worker
|
|
31
|
+
could hang the server indefinitely. The timeout is now enforced, and a worker that times
|
|
32
|
+
out, crashes or desyncs is replaced on the next call.
|
|
33
|
+
|
|
34
|
+
## [0.1.2] — 2026-09-03
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- FastMCP imports updated for the current FastMCP release.
|
|
39
|
+
|
|
40
|
+
## [0.1.1] — 2026-08-06
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- `get_runtime_context` split into `get_allplan_info`, `get_python_env_info` and
|
|
45
|
+
`list_site_packages`.
|
|
46
|
+
- `inspect_symbol` now keeps one worker process alive for the lifetime of the server
|
|
47
|
+
instead of spawning one per call.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Corrected the declared project dependencies.
|
|
52
|
+
|
|
53
|
+
## [0.1.0] — 2026-07-23
|
|
54
|
+
|
|
55
|
+
Initial packaged release, exposing `get_runtime_context`, `typecheck_file` and
|
|
56
|
+
`inspect_symbol`.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# PythonParts Local MCP Server — Developer Documentation
|
|
2
|
+
|
|
3
|
+
## Repository Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
server/
|
|
7
|
+
__init__.py empty
|
|
8
|
+
__main__.py entry point — arg parsing, startup sequence, tool registration
|
|
9
|
+
config.py AllplanConfig dataclass + module-level singleton
|
|
10
|
+
paths.py interpreter_path(), framework_version() and build_sys_path() helpers
|
|
11
|
+
registry.py Windows registry reader → (prg, etc, program_name)
|
|
12
|
+
pyright_config.py writes temp pyrightconfig.json at startup, cleans up on exit
|
|
13
|
+
worker.py PersistentWorker — subprocess lifecycle shared by the worker-backed tools
|
|
14
|
+
tools/
|
|
15
|
+
__init__.py READ_ONLY_TOOL_ANNOTATIONS shared constant
|
|
16
|
+
runtime_context.py get_runtime_context tool
|
|
17
|
+
typecheck.py typecheck_file tool
|
|
18
|
+
inspect_symbol.py inspect_symbol tool
|
|
19
|
+
_introspect_worker.py worker script run under the ALLPLAN interpreter
|
|
20
|
+
run_snippet_in_allplan.py run_snippet_in_allplan tool — NOT REGISTERED, see below
|
|
21
|
+
run_snippet_in_sandbox.py run_snippet_in_sandbox tool
|
|
22
|
+
_sandbox_worker.py worker script run under the ALLPLAN interpreter
|
|
23
|
+
pyproject.toml
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Dependencies
|
|
29
|
+
|
|
30
|
+
Declared in `pyproject.toml`:
|
|
31
|
+
|
|
32
|
+
- `fastmcp` — FastMCP framework (provides `mcp.*` imports)
|
|
33
|
+
|
|
34
|
+
- `pyright` — static type checker (bundled Node binary, invoked via `python -m pyright`)
|
|
35
|
+
- `pywin32` — Windows API bindings (`win32api` used to read the ALLPLAN product version)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Startup Sequence
|
|
40
|
+
|
|
41
|
+
1. Parse CLI args
|
|
42
|
+
2. Read `prg` and `etc` from Windows registry (`HKLM\SOFTWARE\NEMETSCHEK\{product_name}\{version}.0\InstallRoot`)
|
|
43
|
+
3. If registry key missing → print error to stderr and exit 1
|
|
44
|
+
4. Initialise `AllplanConfig` singleton
|
|
45
|
+
5. Write `pyrightconfig.json` to a temp directory; register `atexit` cleanup
|
|
46
|
+
6. Create `FastMCP` instance, register tools, call `mcp.run()`
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## The Four ALLPLAN Paths
|
|
51
|
+
|
|
52
|
+
Everything derives from these four `Path` values stored in `AllplanConfig`:
|
|
53
|
+
|
|
54
|
+
| Field | Source | Example |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `prg` | Registry `ProgramDrive` + `ProgramPath` | `C:\Program Files\Allplan\Allplan 2027\Prg` |
|
|
57
|
+
| `etc` | Registry `ProgramDataDrive` + `ProgramDataPath` + `/etc` | `C:\ProgramData\Nemetschek\Allplan\2027\etc` |
|
|
58
|
+
| `std` | CLI `--std-path` | `C:\Data\Allplan\Allplan 2027\Std` |
|
|
59
|
+
| `usr` | CLI `--usr-path` | `C:\Users\...\Nemetschek\Allplan\2027\Usr\Local` |
|
|
60
|
+
|
|
61
|
+
`paths.py` derives two shared values from these:
|
|
62
|
+
- `interpreter_path(prg)` → `{prg}/Python/python.exe`
|
|
63
|
+
- `build_sys_path(cfg)` → ordered list of 24 paths matching ALLPLAN's runtime `sys.path`
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Tool Implementation Notes
|
|
68
|
+
### Runtime context tools
|
|
69
|
+
|
|
70
|
+
### `get_runtime_context()`
|
|
71
|
+
- `get_allplan_info()` — ALLPLAN version + resolved (prg/etc/std/usr) paths
|
|
72
|
+
|
|
73
|
+
- `get_python_env_info()` — embedded interpreter path, version, and derived sys.path
|
|
74
|
+
|
|
75
|
+
- `list_site_packages()` — installed packages discovered by scanning *site-packages* dirs in sys.path
|
|
76
|
+
|
|
77
|
+
(first occurrence wins, matching Python's import resolution order)
|
|
78
|
+
|
|
79
|
+
### `typecheck_file(file_path: str)`
|
|
80
|
+
|
|
81
|
+
1. Validates the file exists
|
|
82
|
+
2. Calls `pyright --outputjson --project <tmpdir> --pythonpath <interpreter> <file>`
|
|
83
|
+
via `python -m pyright` (server's own venv Python) with `stdin=DEVNULL`
|
|
84
|
+
(critical: prevents pyright from inheriting the MCP server's stdio pipe and hanging)
|
|
85
|
+
3. Extracts JSON from stdout (skips any warning text printed before the `{`)
|
|
86
|
+
4. Filters known false positives before returning
|
|
87
|
+
|
|
88
|
+
**Pyright config** (written to temp dir at startup):
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"stubPath": "{etc}/PythonPartsFramework/InterfaceStubs",
|
|
92
|
+
"extraPaths": ["... full sys_path list ..."],
|
|
93
|
+
"typeCheckingMode": "basic",
|
|
94
|
+
"reportMissingModuleSource": "none"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
The interpreter path is passed as `--pythonpath` (CLI flag), not in the config file —
|
|
98
|
+
`pythonPath` in `pyrightconfig.json` is not recognised by pyright 1.1.x.
|
|
99
|
+
|
|
100
|
+
**Known false positives suppressed:**
|
|
101
|
+
- `reportAttributeAccessIssue` on `BuildingElement` — its attributes are populated
|
|
102
|
+
dynamically from `.pyp` files at runtime and cannot be statically verified
|
|
103
|
+
|
|
104
|
+
`error_count` and `warning_count` are recomputed from the filtered list, so they match the diagnostics array exactly.
|
|
105
|
+
|
|
106
|
+
### Worker-backed tools (`inspect_symbol`, `run_snippet_in_sandbox`)
|
|
107
|
+
|
|
108
|
+
ALLPLAN's `NemAll_Python_*` modules are native extensions that only load in the interpreter
|
|
109
|
+
shipped in `Prg\Python`, so these tools cannot run in-process — they drive a subprocess.
|
|
110
|
+
Loading the DLLs costs a second or two, so `PersistentWorker` spawns the worker on first use
|
|
111
|
+
and keeps it alive for the lifetime of the server.
|
|
112
|
+
|
|
113
|
+
The protocol is one JSON line in, one JSON line out. The first line is an init handshake
|
|
114
|
+
carrying `sys_path`, `prg_plugins`, `allplan_paths_mock` and `etc_pythonparts`; the worker
|
|
115
|
+
sets up the runtime and replies with an ack before accepting requests.
|
|
116
|
+
|
|
117
|
+
The worker's stdout is drained by a pump thread into a queue, which is what allows a read to
|
|
118
|
+
be given a deadline — there is no `select()` on pipes on Windows. Timeouts, crashes and
|
|
119
|
+
protocol desync all kill the worker and return a structured error; the next request spawns a
|
|
120
|
+
fresh process. A snippet that crashes its worker therefore fails, and the call after it succeeds.
|
|
121
|
+
|
|
122
|
+
`_sandbox_worker.py` additionally imports the interface modules under the same aliases the
|
|
123
|
+
Vibe Coding Connector's `/execute` route exposes — minus `document` and `coord_input`, which
|
|
124
|
+
cannot exist without a running ALLPLAN. Each snippet is executed with a fresh copy of that
|
|
125
|
+
globals dict, so nothing leaks between invocations.
|
|
126
|
+
|
|
127
|
+
### `run_snippet_in_allplan` (disabled)
|
|
128
|
+
|
|
129
|
+
Executes a snippet in the user's live ALLPLAN session by POSTing it to the Vibe Coding
|
|
130
|
+
Connector PythonPart's `/execute` endpoint. Unlike the sandbox it has `document` and
|
|
131
|
+
`coord_input`, and can therefore read and modify real project data.
|
|
132
|
+
|
|
133
|
+
Its registration in `__main__.py` is commented out, so it is not exposed by the released
|
|
134
|
+
package and is intentionally absent from `README.md`. Re-enabling it means uncommenting the
|
|
135
|
+
import and the `register_run_snippet_in_allplan_tools(mcp)` call — and documenting it again.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Key Design Decisions
|
|
140
|
+
|
|
141
|
+
| Decision | Rationale |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `stdin=subprocess.DEVNULL` on pyright subprocess | Without this, pyright inherits the MCP server's stdin (the stdio pipe to the MCP client) and hangs indefinitely waiting for input |
|
|
144
|
+
| `pythonPath` as CLI flag, not config key | `pythonPath` in `pyrightconfig.json` is unrecognised in pyright ≥1.1.x; use `--pythonpath` instead |
|
|
145
|
+
| `atexit` for temp dir cleanup | Simpler than FastMCP lifespan hooks; reliable for stdio servers |
|
|
146
|
+
| Filesystem scan instead of `pip list` | ALLPLAN may not have pip; packages are often dropped as folders without going through pip |
|
|
147
|
+
| `sys_path` derived statically | Avoids spawning a subprocess for every `get_runtime_context` call; order matches observed live behaviour |
|
|
148
|
+
| Sandbox runs out-of-process, not in a thread | The ALLPLAN DLLs must be loaded by ALLPLAN's own interpreter, not the server's; a separate process is also what keeps a crashing snippet from taking down the MCP server |
|
|
149
|
+
| Sandbox not gated by `requires_allplan_connector` | It deliberately works with ALLPLAN closed; requiring the connector would defeat the point |
|
|
150
|
+
| Sandbox keeps `MUTATING_TOOL_ANNOTATIONS` | The ALLPLAN document is safe, but the snippet is still arbitrary code with file-system access, so it stays confirmation-gated |
|
|
151
|
+
| Worker stdout drained by a pump thread | Windows cannot `select()` on pipes; queueing the lines is what makes a read deadline possible |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Adding a New Tool
|
|
156
|
+
|
|
157
|
+
1. Create `server/tools/<name>.py`
|
|
158
|
+
2. Define `register_<name>_tools(mcp: FastMCP) -> None` with `@mcp.tool()` decorated functions inside
|
|
159
|
+
3. Import and call the registration function in `server/__main__.py`
|
|
160
|
+
4. Access shared state via `get_config()` (paths) and `get_pyright_config_dir()` / `get_python_interpreter()` (pyright)
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Planned / Not Yet Implemented
|
|
165
|
+
|
|
166
|
+
From the [original design brief](docs/MCP_Python_Environment_Aware_Tooling_Brief.md):
|
|
167
|
+
|
|
168
|
+
- `resolve_import` — confirm a module/symbol is importable; where it resolves
|
|
169
|
+
- `run_tests` — targeted test runner
|
|
170
|
+
- `search_repo_usages` — find in-repo usage patterns for a symbol
|
|
171
|
+
- VS Code extension wrapper for distribution (`.vsix` with bundled server)
|
|
172
|
+
- Response caching for `get_runtime_context` (currently re-computed on every call)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pythonparts-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server exposing the ALLPLAN Python runtime to AI coding assistants
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: fastmcp>=3.0
|
|
7
|
+
Requires-Dist: pyright>=1.1
|
|
8
|
+
Requires-Dist: pywin32>=306; sys_platform == 'win32'
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# PythonParts Local MCP Server
|
|
12
|
+
|
|
13
|
+
An MCP server that exposes the local ALLPLAN Python runtime to AI coding assistants.
|
|
14
|
+
It is started once per ALLPLAN version and gives any MCP-compatible agent accurate,
|
|
15
|
+
live information about the interpreter, import paths, installed packages, and type
|
|
16
|
+
correctness — without guessing or hallucinating.
|
|
17
|
+
|
|
18
|
+
Transport: **stdio** (spawned as a subprocess by the MCP client).
|
|
19
|
+
Protocol: [Model Context Protocol](https://modelcontextprotocol.io/) via `FastMCP`.
|
|
20
|
+
Release history: [CHANGELOG.md](CHANGELOG.md).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
- [**uv**](https://docs.astral.sh/uv/getting-started/installation/) — Python package manager used to run the server without a manual install step.
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
winget install astral-sh.uv
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Attaching to your AI agent
|
|
35
|
+
|
|
36
|
+
Add the following entry to your MCP configuration:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
"pythonparts-local-mcp": {
|
|
40
|
+
"type": "stdio",
|
|
41
|
+
"command": "uvx",
|
|
42
|
+
"args": [
|
|
43
|
+
"--index", "https://test.pypi.org/simple",
|
|
44
|
+
"--index-strategy", "unsafe-best-match",
|
|
45
|
+
"pythonparts-mcp",
|
|
46
|
+
"--allplan-version", "2026",
|
|
47
|
+
"--std-path", "X:\\Path\\To\\Allplan\\Std",
|
|
48
|
+
"--usr-path", "X:\\Path\\To\\Allplan\\Usr\\Local"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Arguments:**
|
|
54
|
+
|
|
55
|
+
| Argument | Description | Example |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| `--allplan-version` | Installed ALLPLAN version | `2026` |
|
|
58
|
+
| `--std-path` | Path to ALLPLAN's `Std` directory | `C:\Data\Allplan\Allplan 2026\Std` |
|
|
59
|
+
| `--usr-path` | Path to ALLPLAN's user directory. Located under `Usr\<username>` or `Usr\Local` depending on your ALLPLAN configuration. | `C:\Users\Jane\Nemetschek\Allplan\2026\Usr\Local` |
|
|
60
|
+
|
|
61
|
+
The `prg` and `etc` paths are resolved automatically from the Windows registry.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Available Tools
|
|
66
|
+
|
|
67
|
+
### `get_allplan_info()`
|
|
68
|
+
|
|
69
|
+
Returns the installed ALLPLAN version string and the four resolved file-system paths (`prg`, `etc`, `std`, `usr`). No input required.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"ok": true,
|
|
74
|
+
"data": {
|
|
75
|
+
"version": "2026.0.0",
|
|
76
|
+
"paths": {
|
|
77
|
+
"prg": "C:\\Program Files\\Allplan\\Allplan 2026\\Prg",
|
|
78
|
+
"etc": "C:\\ProgramData\\Nemetschek\\Allplan\\2026\\etc",
|
|
79
|
+
"std": "X:\\Path\\To\\Allplan\\Std",
|
|
80
|
+
"usr": "X:\\Path\\To\\Allplan\\Usr\\Local"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `get_python_env_info()`
|
|
87
|
+
|
|
88
|
+
Returns the path to the ALLPLAN-embedded Python interpreter, its version, and the ordered `sys.path` ALLPLAN uses when executing PythonParts scripts. No input required.
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"ok": true,
|
|
93
|
+
"data": {
|
|
94
|
+
"interpreter_path": "C:\\...\\Prg\\Python\\python.exe",
|
|
95
|
+
"python_version": "3.11.9",
|
|
96
|
+
"sys_path": ["C:\\...\\Prg\\Python", "..."]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `list_site_packages()`
|
|
102
|
+
|
|
103
|
+
Lists all Python packages installed in the ALLPLAN-embedded Python environment, deduplicated and sorted alphabetically. No input required.
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"ok": true,
|
|
108
|
+
"data": [
|
|
109
|
+
{"name": "requests", "version": "2.31.0"},
|
|
110
|
+
{"name": "shapely", "version": null}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `typecheck_file(file_path: str)`
|
|
116
|
+
|
|
117
|
+
Type-checks a `.py` file against the ALLPLAN runtime using Pyright. Input: absolute path to the file.
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"ok": true,
|
|
122
|
+
"data": {
|
|
123
|
+
"error_count": 2,
|
|
124
|
+
"warning_count": 0,
|
|
125
|
+
"diagnostics": [
|
|
126
|
+
{
|
|
127
|
+
"severity": "error",
|
|
128
|
+
"message": "...",
|
|
129
|
+
"file": "C:\\...\\main.py",
|
|
130
|
+
"line": 12,
|
|
131
|
+
"column": 5,
|
|
132
|
+
"rule": "reportMissingImports"
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `inspect_symbol(symbol: str)`
|
|
140
|
+
|
|
141
|
+
Inspects a Python symbol in the live ALLPLAN runtime — returns its category, signature, docstring, and public members. Use this to verify a symbol exists and discover its API before writing code against it.
|
|
142
|
+
|
|
143
|
+
Input: dotted Python path to the symbol.
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"ok": true,
|
|
148
|
+
"data": {
|
|
149
|
+
"symbol": "NemAll_Python_Geometry.Point3D",
|
|
150
|
+
"category": "class",
|
|
151
|
+
"module": "NemAll_Python_Geometry",
|
|
152
|
+
"docstring": "...",
|
|
153
|
+
"signature": null,
|
|
154
|
+
"members": ["X", "Y", "Z", "..."]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### `run_snippet_in_sandbox(code: str)`
|
|
163
|
+
|
|
164
|
+
Runs a Python snippet against the ALLPLAN libraries in a **separate process, outside ALLPLAN**, and returns its `print()` output. ALLPLAN does not need to be running, and the snippet cannot touch the open drawing or project data. This is the tool to reach for when verifying geometry math or API behaviour.
|
|
165
|
+
|
|
166
|
+
The ALLPLAN interface modules are pre-imported as globals (`AllplanGeometry`, `AllplanBasisElements`, …). There is no document and no coordinate input, so `document` and `coord_input` are **not** available — anything that needs the open drawing, the project or user settings will fail or return a meaningless value.
|
|
167
|
+
|
|
168
|
+
Globals do not persist between calls, so each snippet must be self-contained. The first call spends a second or two loading the ALLPLAN DLLs; the process is then reused. Snippets are abandoned after 60 seconds.
|
|
169
|
+
|
|
170
|
+
Snippets must not show a user interface — `AllplanUtility.ShowMessageBox`, file dialogs and the like. There is no ALLPLAN window to host them, so the call never returns and the snippet runs into the timeout.
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"ok": true,
|
|
175
|
+
"data": {
|
|
176
|
+
"output": "(30000.0, NemAll_Python_Geometry.eServiceResult.POSITIVE_ORIENTATION)\n"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Publishing `pythonparts-mcp`
|
|
2
|
+
|
|
3
|
+
Release uploads authenticate with **PyPI Trusted Publishers (OIDC)**.
|
|
4
|
+
The workflow does not read or pass a PyPI API token.
|
|
5
|
+
|
|
6
|
+
Workflow: [`.github/workflows/publish-pythonparts-mcp.yml`](../.github/workflows/publish-pythonparts-mcp.yml)
|
|
7
|
+
Package: [`pyproject.toml`](pyproject.toml) (`name = "pythonparts-mcp"`)
|
|
8
|
+
|
|
9
|
+
A push to `dev` that changes `local-server/` or the publish workflows builds,
|
|
10
|
+
tests, and uploads the `pythonparts-mcp-dist` artifact for 7 days. That run
|
|
11
|
+
is ungated and publishes nowhere. See
|
|
12
|
+
[`.github/workflows/upload-pythonparts-mcp-dev.yml`](../.github/workflows/upload-pythonparts-mcp-dev.yml).
|
|
13
|
+
|
|
14
|
+
Production publishes only when a `pythonparts-mcp-v*` tag is pushed, and only
|
|
15
|
+
after a `prod` reviewer approves the deployment. The publish job depends on
|
|
16
|
+
the test job, so a failed test, version mismatch, or build does not upload.
|
|
17
|
+
|
|
18
|
+
## Trusted Publisher (exact match)
|
|
19
|
+
|
|
20
|
+
PyPI.org must trust this identity. The environment name is part of the
|
|
21
|
+
OIDC claim, so it has to match the GitHub Environment on the publish job.
|
|
22
|
+
TestPyPI is not used for now.
|
|
23
|
+
|
|
24
|
+
| Field | PyPI.org |
|
|
25
|
+
|---|---|
|
|
26
|
+
| Owner | `Allplan-GmbH` |
|
|
27
|
+
| Repository | `InnovationAI-VibeCoding` |
|
|
28
|
+
| Workflow filename | `publish-pythonparts-mcp.yml` |
|
|
29
|
+
| Environment | `prod` |
|
|
30
|
+
|
|
31
|
+
The publish job uses the existing `prod` environment. Its required reviewers
|
|
32
|
+
are the people who can deploy to prod, and they decide who can publish.
|
|
33
|
+
Under **Deployment branches and tags** for `prod`, allow the tag
|
|
34
|
+
`pythonparts-mcp-v*`. A rule that only allows branch `main` will reject the
|
|
35
|
+
tag and the publish job will not start.
|
|
36
|
+
|
|
37
|
+
The publish job can read secrets stored on the `prod` environment, including
|
|
38
|
+
any AWS secrets used by ECS deploys. This workflow does not use those secrets.
|
|
39
|
+
|
|
40
|
+
### PyPI.org (project does not exist yet)
|
|
41
|
+
|
|
42
|
+
Register a **pending** publisher while signed in as the account that should
|
|
43
|
+
own the project:
|
|
44
|
+
|
|
45
|
+
https://pypi.org/manage/account/publishing/
|
|
46
|
+
|
|
47
|
+
Use environment name `prod`. If a pending publisher was already registered
|
|
48
|
+
with environment `pypi`, delete it and register it again with `prod`.
|
|
49
|
+
Otherwise the upload fails with `invalid-publisher`.
|
|
50
|
+
|
|
51
|
+
The first successful tag publish creates `pythonparts-mcp` on PyPI.org.
|
|
52
|
+
Do not create the project with an API token first; that skips the pending
|
|
53
|
+
publisher.
|
|
54
|
+
|
|
55
|
+
Docs: https://docs.pypi.org/trusted-publishers/
|
|
56
|
+
|
|
57
|
+
## Retire the API token
|
|
58
|
+
|
|
59
|
+
After the first PyPI.org upload from this workflow succeeds:
|
|
60
|
+
|
|
61
|
+
1. Delete every `PYPI_*` and `TEST_PYPI_*` API token secret from this
|
|
62
|
+
repository and from the `Allplan-GmbH` organization Actions secrets.
|
|
63
|
+
2. Revoke that token on PyPI and TestPyPI (Account settings → API tokens).
|
|
64
|
+
3. Remove the service account that minted the token from Owners and
|
|
65
|
+
Maintainers on the `pythonparts-mcp` project. A human owner has to remain,
|
|
66
|
+
or the project is left with nobody who can manage it.
|
|
67
|
+
|
|
68
|
+
## How to publish
|
|
69
|
+
|
|
70
|
+
### Dev build (artifact)
|
|
71
|
+
|
|
72
|
+
A push to `dev` that changes this package starts **Upload pythonparts-mcp from dev**.
|
|
73
|
+
Download `pythonparts-mcp-dist` from that run's artifacts. The upload is
|
|
74
|
+
ungated, kept for 7 days, and does not require a version bump. It does not
|
|
75
|
+
publish to a package index.
|
|
76
|
+
|
|
77
|
+
### Production
|
|
78
|
+
|
|
79
|
+
1. Set `pyproject.toml` to the release version, for example `0.3.0`.
|
|
80
|
+
2. Tag that exact version and push the tag:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git tag pythonparts-mcp-v0.3.0
|
|
84
|
+
git push origin pythonparts-mcp-v0.3.0
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
3. After a `prod` reviewer approves the deployment, the workflow publishes to
|
|
88
|
+
https://pypi.org/project/pythonparts-mcp/ only when the tag version equals
|
|
89
|
+
`project.version`.
|
|
90
|
+
|
|
91
|
+
Manual runs never publish to any package index.
|