archicad-mcp-server 0.5.4__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.
Files changed (140) hide show
  1. archicad_mcp_server-0.5.4/.gitattributes +15 -0
  2. archicad_mcp_server-0.5.4/.github/workflows/publish.yml +233 -0
  3. archicad_mcp_server-0.5.4/.github/workflows/test.yml +43 -0
  4. archicad_mcp_server-0.5.4/.gitignore +23 -0
  5. archicad_mcp_server-0.5.4/LICENSE +21 -0
  6. archicad_mcp_server-0.5.4/PKG-INFO +595 -0
  7. archicad_mcp_server-0.5.4/PRIVACY.md +105 -0
  8. archicad_mcp_server-0.5.4/README.md +579 -0
  9. archicad_mcp_server-0.5.4/docs/api-dashboard.html +519 -0
  10. archicad_mcp_server-0.5.4/docs/gdl-pipeline.md +124 -0
  11. archicad_mcp_server-0.5.4/docs/index.md +39 -0
  12. archicad_mcp_server-0.5.4/docs/known-issues.md +282 -0
  13. archicad_mcp_server-0.5.4/docs/query.md +242 -0
  14. archicad_mcp_server-0.5.4/docs/rules.md +248 -0
  15. archicad_mcp_server-0.5.4/docs/scheme-criteria-codes.md +83 -0
  16. archicad_mcp_server-0.5.4/docs/superpowers/plans/2026-07-28-schedule-scheme-editing.md +2305 -0
  17. archicad_mcp_server-0.5.4/docs/superpowers/plans/2026-09-01-railing-creation.md +912 -0
  18. archicad_mcp_server-0.5.4/docs/superpowers/plans/2026-09-02-gdl-mcp-tools.md +1798 -0
  19. archicad_mcp_server-0.5.4/docs/superpowers/specs/2026-07-16-archicad-mcp-design.md +265 -0
  20. archicad_mcp_server-0.5.4/docs/superpowers/specs/2026-07-28-schedule-scheme-editing-design.md +267 -0
  21. archicad_mcp_server-0.5.4/docs/superpowers/specs/2026-09-01-railing-creation-design.md +264 -0
  22. archicad_mcp_server-0.5.4/docs/superpowers/specs/2026-09-02-gdl-mcp-tools-design.md +270 -0
  23. archicad_mcp_server-0.5.4/docs/superpowers/specs/2026-09-03-criteria-query-design.md +86 -0
  24. archicad_mcp_server-0.5.4/icon.png +0 -0
  25. archicad_mcp_server-0.5.4/icon.svg +17 -0
  26. archicad_mcp_server-0.5.4/manifest.json +258 -0
  27. archicad_mcp_server-0.5.4/pyproject.toml +36 -0
  28. archicad_mcp_server-0.5.4/scripts/__init__.py +0 -0
  29. archicad_mcp_server-0.5.4/scripts/build_bundle.py +286 -0
  30. archicad_mcp_server-0.5.4/scripts/build_dashboard.py +796 -0
  31. archicad_mcp_server-0.5.4/scripts/check_release_version.py +204 -0
  32. archicad_mcp_server-0.5.4/scripts/diff_scheme_criteria.py +120 -0
  33. archicad_mcp_server-0.5.4/scripts/make_icon.py +83 -0
  34. archicad_mcp_server-0.5.4/scripts/sync_tapir_defs.py +63 -0
  35. archicad_mcp_server-0.5.4/server.json +31 -0
  36. archicad_mcp_server-0.5.4/src/archicad_mcp/__init__.py +0 -0
  37. archicad_mcp_server-0.5.4/src/archicad_mcp/actions.py +34 -0
  38. archicad_mcp_server-0.5.4/src/archicad_mcp/connection.py +214 -0
  39. archicad_mcp_server-0.5.4/src/archicad_mcp/core/__init__.py +0 -0
  40. archicad_mcp_server-0.5.4/src/archicad_mcp/core/attributes.py +31 -0
  41. archicad_mcp_server-0.5.4/src/archicad_mcp/core/create.py +30 -0
  42. archicad_mcp_server-0.5.4/src/archicad_mcp/core/definitions.py +283 -0
  43. archicad_mcp_server-0.5.4/src/archicad_mcp/core/element_data.py +98 -0
  44. archicad_mcp_server-0.5.4/src/archicad_mcp/core/issues.py +53 -0
  45. archicad_mcp_server-0.5.4/src/archicad_mcp/core/mutate.py +25 -0
  46. archicad_mcp_server-0.5.4/src/archicad_mcp/core/project.py +50 -0
  47. archicad_mcp_server-0.5.4/src/archicad_mcp/core/publish.py +8 -0
  48. archicad_mcp_server-0.5.4/src/archicad_mcp/core/query.py +250 -0
  49. archicad_mcp_server-0.5.4/src/archicad_mcp/core/query_schema.py +88 -0
  50. archicad_mcp_server-0.5.4/src/archicad_mcp/core/schemes.py +341 -0
  51. archicad_mcp_server-0.5.4/src/archicad_mcp/core/selection.py +36 -0
  52. archicad_mcp_server-0.5.4/src/archicad_mcp/core/teamwork.py +131 -0
  53. archicad_mcp_server-0.5.4/src/archicad_mcp/criteria.py +467 -0
  54. archicad_mcp_server-0.5.4/src/archicad_mcp/extract.py +372 -0
  55. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/__init__.py +0 -0
  56. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/definitions/command_definitions.js +10875 -0
  57. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/definitions/common_schema_definitions.js +8087 -0
  58. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/definitions/local_commands.json +42 -0
  59. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/definitions/tapir_version.json +5 -0
  60. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/execute.py +149 -0
  61. archicad_mcp_server-0.5.4/src/archicad_mcp/gateway/registry.py +131 -0
  62. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/__init__.py +7 -0
  63. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/cli.py +160 -0
  64. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/config.py +160 -0
  65. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/deploy.py +89 -0
  66. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/generate.py +472 -0
  67. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/mesh.py +265 -0
  68. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/toolchain.py +229 -0
  69. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/tools.py +366 -0
  70. archicad_mcp_server-0.5.4/src/archicad_mcp/gdl/workspace.py +57 -0
  71. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/__init__.py +0 -0
  72. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/__init__.py +0 -0
  73. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/base.py +95 -0
  74. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/classification_required.py +48 -0
  75. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/ifc_readiness.py +57 -0
  76. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/layer_compliance.py +64 -0
  77. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/property_required.py +48 -0
  78. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/builtin/zone_checks.py +39 -0
  79. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/engine.py +73 -0
  80. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/examples/example-rules.yaml +20 -0
  81. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/loader.py +92 -0
  82. archicad_mcp_server-0.5.4/src/archicad_mcp/rules/types.py +67 -0
  83. archicad_mcp_server-0.5.4/src/archicad_mcp/schemes/__init__.py +0 -0
  84. archicad_mcp_server-0.5.4/src/archicad_mcp/schemes/columns.py +208 -0
  85. archicad_mcp_server-0.5.4/src/archicad_mcp/schemes/model.py +226 -0
  86. archicad_mcp_server-0.5.4/src/archicad_mcp/schemes/spec.py +364 -0
  87. archicad_mcp_server-0.5.4/src/archicad_mcp/schemes/validate.py +87 -0
  88. archicad_mcp_server-0.5.4/src/archicad_mcp/schemes/xml_io.py +118 -0
  89. archicad_mcp_server-0.5.4/src/archicad_mcp/server.py +768 -0
  90. archicad_mcp_server-0.5.4/tests/__init__.py +0 -0
  91. archicad_mcp_server-0.5.4/tests/conftest.py +25 -0
  92. archicad_mcp_server-0.5.4/tests/fixtures/__init__.py +0 -0
  93. archicad_mcp_server-0.5.4/tests/fixtures/api_replays.py +234 -0
  94. archicad_mcp_server-0.5.4/tests/fixtures/schemes/sample_scheme.xml +114 -0
  95. archicad_mcp_server-0.5.4/tests/rules/__init__.py +0 -0
  96. archicad_mcp_server-0.5.4/tests/rules/test_applies_to_where.py +59 -0
  97. archicad_mcp_server-0.5.4/tests/rules/test_builtin_layers_zones_ifc.py +64 -0
  98. archicad_mcp_server-0.5.4/tests/rules/test_builtin_property_classification.py +64 -0
  99. archicad_mcp_server-0.5.4/tests/rules/test_engine.py +127 -0
  100. archicad_mcp_server-0.5.4/tests/rules/test_loader.py +84 -0
  101. archicad_mcp_server-0.5.4/tests/rules/test_types.py +37 -0
  102. archicad_mcp_server-0.5.4/tests/schemes/__init__.py +0 -0
  103. archicad_mcp_server-0.5.4/tests/schemes/conftest.py +102 -0
  104. archicad_mcp_server-0.5.4/tests/schemes/test_columns.py +394 -0
  105. archicad_mcp_server-0.5.4/tests/schemes/test_core_edit.py +665 -0
  106. archicad_mcp_server-0.5.4/tests/schemes/test_core_read.py +134 -0
  107. archicad_mcp_server-0.5.4/tests/schemes/test_criteria_diff.py +71 -0
  108. archicad_mcp_server-0.5.4/tests/schemes/test_model.py +234 -0
  109. archicad_mcp_server-0.5.4/tests/schemes/test_spec.py +636 -0
  110. archicad_mcp_server-0.5.4/tests/schemes/test_validate.py +173 -0
  111. archicad_mcp_server-0.5.4/tests/schemes/test_xml_io.py +232 -0
  112. archicad_mcp_server-0.5.4/tests/test_cli_transport.py +75 -0
  113. archicad_mcp_server-0.5.4/tests/test_connection.py +204 -0
  114. archicad_mcp_server-0.5.4/tests/test_criteria.py +276 -0
  115. archicad_mcp_server-0.5.4/tests/test_definitions.py +148 -0
  116. archicad_mcp_server-0.5.4/tests/test_element_coverage.py +134 -0
  117. archicad_mcp_server-0.5.4/tests/test_extract.py +187 -0
  118. archicad_mcp_server-0.5.4/tests/test_gateway.py +230 -0
  119. archicad_mcp_server-0.5.4/tests/test_gdl_config.py +61 -0
  120. archicad_mcp_server-0.5.4/tests/test_gdl_deploy.py +44 -0
  121. archicad_mcp_server-0.5.4/tests/test_gdl_generate.py +182 -0
  122. archicad_mcp_server-0.5.4/tests/test_gdl_mesh.py +100 -0
  123. archicad_mcp_server-0.5.4/tests/test_gdl_toolchain.py +75 -0
  124. archicad_mcp_server-0.5.4/tests/test_gdl_tools.py +720 -0
  125. archicad_mcp_server-0.5.4/tests/test_gdl_workspace.py +70 -0
  126. archicad_mcp_server-0.5.4/tests/test_live.py +282 -0
  127. archicad_mcp_server-0.5.4/tests/test_manifest.py +25 -0
  128. archicad_mcp_server-0.5.4/tests/test_query.py +207 -0
  129. archicad_mcp_server-0.5.4/tests/test_query_schema.py +89 -0
  130. archicad_mcp_server-0.5.4/tests/test_release_version.py +200 -0
  131. archicad_mcp_server-0.5.4/tests/test_server_gdl_wiring.py +39 -0
  132. archicad_mcp_server-0.5.4/tests/test_server_smoke.py +29 -0
  133. archicad_mcp_server-0.5.4/tests/test_startup_banner.py +245 -0
  134. archicad_mcp_server-0.5.4/tests/test_teamwork.py +165 -0
  135. archicad_mcp_server-0.5.4/tests/test_tier1_tools.py +224 -0
  136. archicad_mcp_server-0.5.4/tests/test_tier2_mutations.py +128 -0
  137. archicad_mcp_server-0.5.4/tests/test_tier2_project_issues.py +132 -0
  138. archicad_mcp_server-0.5.4/tests/test_tier2_query_data.py +142 -0
  139. archicad_mcp_server-0.5.4/tests/test_tool_annotations.py +143 -0
  140. archicad_mcp_server-0.5.4/uv.lock +1538 -0
@@ -0,0 +1,15 @@
1
+ # Scheme XML is compared byte for byte. tests/schemes/test_xml_io.py asserts
2
+ # that a load/save cycle reproduces tests/fixtures/schemes/sample_scheme.xml
3
+ # exactly, and round_trips_exactly() treats a newline difference as a rewrite
4
+ # it must refuse. If a checkout converted these files to CRLF (Git for Windows
5
+ # installs with core.autocrlf=true by default, and actions/checkout does not
6
+ # override it), the fixture on disk would no longer be the bytes those tests
7
+ # were written against. Pin it so every platform sees the same file.
8
+ *.xml text eol=lf
9
+
10
+ # Same reasoning for the YAML specs and rule files the suite reads, and for
11
+ # Python sources, so nothing in the tree depends on the checkout's newline
12
+ # policy.
13
+ *.yaml text eol=lf
14
+ *.yml text eol=lf
15
+ *.py text eol=lf
@@ -0,0 +1,233 @@
1
+ name: release
2
+ on:
3
+ push:
4
+ # Version tags only. A tag is the one signal that says "this exact commit
5
+ # is 0.1.0", and nothing else in the repository should be able to publish.
6
+ tags: ["v*"]
7
+
8
+ # Two privileged things happen here, and this is the whole grant for both.
9
+ # contents creates the release. id-token mints a short-lived OIDC token that
10
+ # PyPI and the MCP registry each accept as proof this run is from this
11
+ # repository, which is why publishing to either needs no stored credential:
12
+ # there is nothing to rotate, nothing to leak, and a token that is worthless
13
+ # minutes later. PyPI matches the token against this file's name, so renaming
14
+ # publish.yml silently ends PyPI publishing until the trusted publisher on
15
+ # pypi.org is re-registered to match. Every other step reads the checkout and
16
+ # writes to the runner.
17
+ permissions:
18
+ contents: write
19
+ id-token: write
20
+
21
+ jobs:
22
+ release:
23
+ runs-on: ubuntu-latest
24
+ env:
25
+ # One Python, not test.yml's matrix. The wheel is py3-none-any and the
26
+ # bundle ships pyproject.toml and uv.lock rather than vendored wheels,
27
+ # so nothing that gets uploaded depends on the interpreter that built
28
+ # it. 3.13 is the newer of the two the suite covers, so the release is
29
+ # still built on a version that is actually tested.
30
+ UV_PYTHON: "3.13"
31
+ steps:
32
+ # The same two pins as test.yml, for the reasons written out there:
33
+ # checkout follows its major, setup-uv is an exact version because
34
+ # astral-sh stopped publishing major tags after v7. Move the two
35
+ # workflows together, so a release is built by the toolchain that
36
+ # tested the commit rather than a later one nothing has run yet.
37
+ # persist-credentials defaults to true, which writes this run's
38
+ # GITHUB_TOKEN into .git/config as an auth header. This job holds
39
+ # contents: write and then runs code that is not ours (npx fetches the
40
+ # mcpb packer, uv sync resolves the whole PyPI tree), so that would leave
41
+ # a write-capable token on disk alongside it. Nothing here pushes over
42
+ # git: gh release create authenticates through GH_TOKEN instead.
43
+ - uses: actions/checkout@v7
44
+ with:
45
+ persist-credentials: false
46
+ - uses: astral-sh/setup-uv@v9.0.0
47
+ with:
48
+ # Same reason test.yml gives every matrix entry its own key. The
49
+ # default one is derived from the runner image's default Python, not
50
+ # from UV_PYTHON, so without a suffix this job would race a test run
51
+ # for the one key that image hands both of them.
52
+ cache-suffix: release
53
+
54
+ # The gate, before anything is synced, built, or packed. It is the one
55
+ # failure here whose cost is not a re-run: a tag has already been pushed
56
+ # by the time this workflow exists, so a mismatch means deleting the tag
57
+ # and pushing it again, and every second spent before saying so is spent
58
+ # on work that is going to be thrown away. --no-project because the
59
+ # check imports nothing outside the standard library.
60
+ #
61
+ # $GITHUB_REF_NAME rather than ${{ github.ref_name }}: a tag name is text
62
+ # somebody chose, and ${{ }} substitution pastes it into the script
63
+ # before the shell parses it. Through the environment it stays one
64
+ # argument no matter what it contains.
65
+ - run: uv run --no-project python scripts/check_release_version.py "$GITHUB_REF_NAME"
66
+
67
+ # The version lives in a fourth place that check cannot see: uv.lock
68
+ # records the project's own version, and the bundle SHIPS uv.lock. Bump
69
+ # pyproject.toml, manifest.json and the README without re-running
70
+ # uv lock, and the check above happily says "agree" while the extension
71
+ # goes out with a lock file that disagrees with its own pyproject, which
72
+ # makes uv re-resolve on the user's machine at first launch. uv lock
73
+ # --check is the purpose-built test for exactly that, and is a sub-second
74
+ # no-op when they agree. It has to run before uv sync, because sync
75
+ # quietly updates the lock and would erase the evidence.
76
+ - run: uv lock --check
77
+
78
+ # test.yml has already run the full matrix if this commit reached main
79
+ # the usual way, but a tag can be pushed at any commit, including one
80
+ # that never did. This is the tripwire for that case, and is deliberately
81
+ # a single run rather than a second copy of the matrix, which would only
82
+ # re-prove what main already proved.
83
+ - run: uv sync
84
+ - run: uv run pytest -v
85
+
86
+ # Wheel and sdist first, because uv build is also what creates dist/ for
87
+ # the bundle to be written into. dist/ is not in the repository.
88
+ - run: uv build
89
+
90
+ # One bundle per platform, all three cross-built on this one runner. The
91
+ # bundles are platform-specific because the dependency tree contains
92
+ # compiled wheels; the build is not, because uv resolves wheels for a
93
+ # named target platform and the standalone interpreters are plain
94
+ # downloads. That is what keeps this a single job rather than a runner
95
+ # matrix, at the cost of the one check a matrix would give for free:
96
+ # nothing here can execute a Windows bundle, so a release is not proven
97
+ # until one is installed on Windows. The script validates each generated
98
+ # manifest before packing it, and pins the mcpb packer for the reason
99
+ # pins exist here generally: the packer decides the bundle format, so it
100
+ # is the thing worth holding still between releases.
101
+ #
102
+ # Node is whatever the runner image ships, an LTS it keeps current. It
103
+ # runs the packer; it does not decide anything the packer produces.
104
+ - run: uv run python scripts/build_bundle.py --target all
105
+
106
+ # server.json tells the registry where each bundle is and what it must
107
+ # hash to, and clients check that hash before they install. Every other
108
+ # field in it is committed and gated by check_release_version.py, the
109
+ # same as manifest.json. These cannot be: they are hashes of bundles
110
+ # built from the very commit that would have to contain them. So they are
111
+ # stamped at the first moment they are knowable, and the placeholder that
112
+ # is committed is never what gets published.
113
+ #
114
+ # Matched on the filename rather than by position, so that reordering
115
+ # server.json's packages cannot quietly pair a bundle with another
116
+ # platform's hash. The check afterwards is what makes that safe to rely
117
+ # on: a package left holding the placeholder is refused by every client
118
+ # that checks the hash, and an unstamped entry is the only way that
119
+ # happens by accident.
120
+ - run: |
121
+ version="${GITHUB_REF_NAME#v}"
122
+ for target in win32 darwin-arm64; do
123
+ name="archicad-mcp-${version}-${target}.mcpb"
124
+ sha="$(sha256sum "dist/$name" | cut -d' ' -f1)"
125
+ jq --arg name "$name" --arg sha "$sha" \
126
+ '(.packages[] | select(.identifier | endswith($name))).fileSha256 = $sha' \
127
+ server.json > server.stamped
128
+ mv server.stamped server.json
129
+ done
130
+ if jq -e '[.packages[] | select(.fileSha256 | test("^0{64}$"))] | length > 0' \
131
+ server.json > /dev/null; then
132
+ echo "::error::a package in server.json kept its placeholder hash" >&2
133
+ exit 1
134
+ fi
135
+
136
+ # Anything that is not exactly major.minor.patch is a prerelease,
137
+ # whichever way it is spelled (v0.2.0rc1, v0.2.0-beta.1). Decided once,
138
+ # here, because the two steps below need the same answer and a second
139
+ # copy of this regex would be a second thing to keep in step.
140
+ - run: |
141
+ if [[ "${GITHUB_REF_NAME#v}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
142
+ echo "PRERELEASE=false" >> "$GITHUB_ENV"
143
+ else
144
+ echo "PRERELEASE=true" >> "$GITHUB_ENV"
145
+ fi
146
+
147
+ # gh is preinstalled on the runner and is the same command this would be
148
+ # by hand, so there is no third-party release action to pin and keep
149
+ # current. The wheel and sdist go up alongside the bundle as well as to
150
+ # PyPI, so that a pinned install can name a release asset by URL without
151
+ # depending on a second service. A prerelease is labelled as one rather
152
+ # than offered from the releases page as the current version to download.
153
+ #
154
+ # Create, or re-upload if the release already exists. The registry publish
155
+ # below is the last step and the one most likely to fail on its own terms,
156
+ # since it talks to a service still in preview. When it did, the run could
157
+ # not simply be re-run: this step had already created the release, and
158
+ # creating it again is an error, so the recovery the comment below
159
+ # promises was not actually available. Now it is.
160
+ - run: |
161
+ flags=(--generate-notes --title "$GITHUB_REF_NAME")
162
+ if [[ "$PRERELEASE" == "true" ]]; then
163
+ flags+=(--prerelease)
164
+ fi
165
+ if gh release view "$GITHUB_REF_NAME" > /dev/null 2>&1; then
166
+ echo "release exists, replacing its assets"
167
+ gh release upload "$GITHUB_REF_NAME" --clobber \
168
+ dist/*.mcpb dist/*.whl dist/*.tar.gz
169
+ else
170
+ gh release create "$GITHUB_REF_NAME" "${flags[@]}" \
171
+ dist/*.mcpb dist/*.whl dist/*.tar.gz
172
+ fi
173
+ env:
174
+ GH_TOKEN: ${{ github.token }}
175
+
176
+ # The wheel and sdist, and only those: dist/ also holds the bundles by
177
+ # now, and uv publish would try to upload whatever it is handed. Named
178
+ # archicad-mcp-server on PyPI because archicad-mcp was already taken
179
+ # there by an unrelated project; the import package, the extension and
180
+ # the registry entry all keep the shorter name, so the distribution name
181
+ # in pyproject.toml is the only place the two differ.
182
+ #
183
+ # uv publish does trusted publishing natively over the id-token grant
184
+ # above, which is what keeps this a plain command rather than a
185
+ # third-party action to pin, the same reason gh is used for the release.
186
+ # "always" rather than the default "automatic" so that a run that cannot
187
+ # get a token fails here with that reason, instead of falling through to
188
+ # a password prompt that has nothing to read from.
189
+ #
190
+ # --check-url is what makes a re-run survivable. PyPI refuses a file it
191
+ # already holds, and the registry step below is the one most likely to
192
+ # fail, so without it every registry failure would also be a PyPI
193
+ # failure on the retry. With it, files already on the index are skipped
194
+ # and the run proceeds to the step that actually needs re-running.
195
+ #
196
+ # Prereleases go up too. PyPI understands them, and pip and uv leave
197
+ # them alone unless asked, so publishing one hands nobody a release
198
+ # candidate by default, which is the reason the registry step skips them.
199
+ - name: Publish to PyPI
200
+ run: |
201
+ uv publish --trusted-publishing always \
202
+ --check-url https://pypi.org/simple/ \
203
+ dist/*.whl dist/*.tar.gz
204
+
205
+ # After the release exists, never before. The registry stores the
206
+ # bundle's download URL and hands it to clients verbatim, so publishing
207
+ # while the asset was still uploading would advertise a URL that 404s,
208
+ # and nothing would tell the registry to look again later.
209
+ #
210
+ # Skipped for prereleases. The registry offers one current version per
211
+ # server, so registering v0.2.0rc1 would hand a release candidate to
212
+ # everyone who installs from a client's server list.
213
+ #
214
+ # Unpinned, unlike the mcpb packer above, and for the opposite reason.
215
+ # The packer decides the format of a file this repository produces, so
216
+ # holding it still is what keeps bundles comparable between releases.
217
+ # mcp-publisher speaks a protocol the registry defines and is still
218
+ # changing while it is in preview, so the version that works is the one
219
+ # matching the registry as it is today, not as it was at the last release.
220
+ #
221
+ # Last, so that nothing else in the release depends on it. A failure here
222
+ # leaves the GitHub release intact and correct, which is what every
223
+ # install path other than the registry uses, and re-running the job
224
+ # recovers it. What re-running cannot do is correct a publish that
225
+ # succeeded: the registry refuses a version it already holds ("cannot
226
+ # publish duplicate version"), so a wrong entry is fixed by releasing the
227
+ # next patch version, never by pushing this tag again.
228
+ - name: Publish to the MCP registry
229
+ if: env.PRERELEASE == 'false'
230
+ run: |
231
+ curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
232
+ ./mcp-publisher login github-oidc
233
+ ./mcp-publisher publish
@@ -0,0 +1,43 @@
1
+ name: test
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ os: [ubuntu-latest, macos-latest, windows-latest]
13
+ python-version: ["3.12", "3.13"]
14
+ runs-on: ${{ matrix.os }}
15
+ env:
16
+ UV_PYTHON: ${{ matrix.python-version }}
17
+ steps:
18
+ # Both pinned to versions that run on Node 24. checkout moved to Node 24
19
+ # in v5 and setup-uv in v7; the older v4/v5 pair here ran on Node 20,
20
+ # which the runners now force onto Node 24 anyway while emitting a
21
+ # deprecation warning on every job.
22
+ - uses: actions/checkout@v7
23
+ # Exact version, not a floating major like the line above, because
24
+ # astral-sh stopped publishing major tags after v7: there is no v8 or v9
25
+ # to track, so the alternatives are pinning a full version or sitting two
26
+ # majors behind on v7. Bump this by hand (or let Dependabot do it).
27
+ - uses: astral-sh/setup-uv@v9.0.0
28
+ with:
29
+ # The default cache key carries the runner image's own default Python
30
+ # (3.14.6 on the macOS image, for instance), not UV_PYTHON, so both
31
+ # matrix entries for a given OS land on one key and race to reserve
32
+ # it: one saves and the other warns that another job may be creating
33
+ # the cache. The suffix gives every entry its own key.
34
+ cache-suffix: ${{ matrix.python-version }}
35
+ # Before uv sync, which quietly updates the lock and would erase the
36
+ # evidence. uv.lock records the project's own version and ships inside
37
+ # the .mcpb bundle, so a bump that forgot to re-lock is caught here, on
38
+ # the commit that caused it, rather than at tag time when the fix is to
39
+ # delete a pushed tag.
40
+ - run: uv lock --check
41
+ - run: uv sync
42
+ - run: uv run pytest -v
43
+ - run: uv build
@@ -0,0 +1,23 @@
1
+ .claude/settings.local.json
2
+ __pycache__/
3
+ .venv/
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .DS_Store
7
+
8
+ # Real project files used to exercise the server against a live model: survey
9
+ # points, CAD exports, a .pln. They are someone's drawings rather than
10
+ # fixtures, and this repository is public, so they stay on the machine that
11
+ # needs them. Test data that belongs here goes in tests/fixtures/.
12
+ teren/
13
+
14
+ # Staging tree and cached standalone interpreters for scripts/build_bundle.py.
15
+ # Both are rebuildable from pinned inputs, and the interpreter cache alone is
16
+ # tens of megabytes per target.
17
+ build/
18
+
19
+ # Isolated worktrees for parallel work, and the subagent-driven-development
20
+ # scratch area (ledgers, task briefs, review packages). Both are working state
21
+ # rebuilt on demand; the git history is the durable record.
22
+ .worktrees/
23
+ .superpowers/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aleš Dolenec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.