fastmcp-extensions 0.1.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.
Files changed (40) hide show
  1. fastmcp_extensions-0.1.0/.github/dependabot.yml +35 -0
  2. fastmcp_extensions-0.1.0/.github/pr-welcome-message.md +32 -0
  3. fastmcp_extensions-0.1.0/.github/release-drafter.yml +81 -0
  4. fastmcp_extensions-0.1.0/.github/workflows/autofix-command.yml +37 -0
  5. fastmcp_extensions-0.1.0/.github/workflows/format-check.yml +31 -0
  6. fastmcp_extensions-0.1.0/.github/workflows/lint-check.yml +54 -0
  7. fastmcp_extensions-0.1.0/.github/workflows/lock-command.yml +37 -0
  8. fastmcp_extensions-0.1.0/.github/workflows/poe-command.yml +34 -0
  9. fastmcp_extensions-0.1.0/.github/workflows/pr-welcome.yml +23 -0
  10. fastmcp_extensions-0.1.0/.github/workflows/prerelease-command.yml +117 -0
  11. fastmcp_extensions-0.1.0/.github/workflows/publish.yml +199 -0
  12. fastmcp_extensions-0.1.0/.github/workflows/pytest-fast.yml +69 -0
  13. fastmcp_extensions-0.1.0/.github/workflows/pytest-matrix.yml +52 -0
  14. fastmcp_extensions-0.1.0/.github/workflows/release-drafter.yml +90 -0
  15. fastmcp_extensions-0.1.0/.github/workflows/semantic-pr-check.yml +112 -0
  16. fastmcp_extensions-0.1.0/.github/workflows/slash-command-dispatch.yml +53 -0
  17. fastmcp_extensions-0.1.0/.github/workflows/tk-todo-check.yml +45 -0
  18. fastmcp_extensions-0.1.0/.gitignore +210 -0
  19. fastmcp_extensions-0.1.0/.python-version +1 -0
  20. fastmcp_extensions-0.1.0/CONTRIBUTING.md +23 -0
  21. fastmcp_extensions-0.1.0/LICENSE +201 -0
  22. fastmcp_extensions-0.1.0/PKG-INFO +423 -0
  23. fastmcp_extensions-0.1.0/README.md +192 -0
  24. fastmcp_extensions-0.1.0/poe_tasks.toml +49 -0
  25. fastmcp_extensions-0.1.0/pyproject.toml +99 -0
  26. fastmcp_extensions-0.1.0/pytest.ini +26 -0
  27. fastmcp_extensions-0.1.0/ruff.toml +57 -0
  28. fastmcp_extensions-0.1.0/src/fastmcp_extensions/__init__.py +54 -0
  29. fastmcp_extensions-0.1.0/src/fastmcp_extensions/annotations.py +51 -0
  30. fastmcp_extensions-0.1.0/src/fastmcp_extensions/decorators.py +223 -0
  31. fastmcp_extensions-0.1.0/src/fastmcp_extensions/prompts.py +87 -0
  32. fastmcp_extensions-0.1.0/src/fastmcp_extensions/py.typed +0 -0
  33. fastmcp_extensions-0.1.0/src/fastmcp_extensions/registration.py +220 -0
  34. fastmcp_extensions-0.1.0/src/fastmcp_extensions/utils/__init__.py +40 -0
  35. fastmcp_extensions-0.1.0/src/fastmcp_extensions/utils/_http.py +32 -0
  36. fastmcp_extensions-0.1.0/src/fastmcp_extensions/utils/describe_server.py +192 -0
  37. fastmcp_extensions-0.1.0/src/fastmcp_extensions/utils/test_tool.py +211 -0
  38. fastmcp_extensions-0.1.0/tests/__init__.py +1 -0
  39. fastmcp_extensions-0.1.0/tests/test_fastmcp_extensions.py +126 -0
  40. fastmcp_extensions-0.1.0/uv.lock +2217 -0
@@ -0,0 +1,35 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: pip
9
+ directory: "/" # Location of package manifests
10
+ commit-message:
11
+ prefix: "chore(deps): "
12
+ schedule:
13
+ interval: daily
14
+ labels:
15
+ - chore
16
+ - dependencies
17
+ open-pull-requests-limit: 8 # default is 5
18
+
19
+ - package-ecosystem: github-actions
20
+ open-pull-requests-limit: 5 # default is 5
21
+ directory: "/"
22
+ commit-message:
23
+ prefix: "ci(deps): "
24
+ schedule:
25
+ interval: monthly
26
+ labels:
27
+ - ci
28
+ - dependencies
29
+ groups:
30
+ # allow combining github-actions updates into a single PR
31
+ minor-and-patch:
32
+ applies-to: version-updates
33
+ update-types:
34
+ - patch
35
+ - minor
@@ -0,0 +1,32 @@
1
+ 🎉 **Thanks for opening this pull request!**
2
+
3
+ Your contribution is appreciated. Here are some helpful commands you can use:
4
+
5
+ ## Quick Commands
6
+ - `/autofix` - Auto-format and fix linting issues (ruff format + ruff check --fix)
7
+ - `/lock` - Update the uv.lock file with latest dependencies
8
+
9
+ ## Available Poe Tasks
10
+ You can run any of these tasks using the slash command: `/poe <task-name>`
11
+
12
+ ### Core Tasks
13
+ - `/poe test` - Run all tests
14
+ - `/poe test-fast` - Run tests with fast exit on first failure
15
+ - `/poe lint` - Check code style and quality
16
+ - `/poe format` - Format code with ruff
17
+ - `/poe deps` - Check for unused and missing dependencies
18
+ - `/poe check` - Run format check, linting, dependency check, and tests
19
+
20
+ ### Quick Fixes
21
+ - `/poe fix` - Auto-format and fix linting issues
22
+ - `/poe clean` - Clean up build artifacts and cache
23
+
24
+ ### Build & Install
25
+ - `/poe build` - Build the package
26
+ - `/poe install` - Install with development dependencies
27
+
28
+ ### Other Commands
29
+ - `/poe version` - Show package version
30
+ - `/poe pre-commit` - Run pre-commit style checks
31
+
32
+ The CI will automatically run tests when you push commits. Happy coding! 🚀
@@ -0,0 +1,81 @@
1
+ name-template: "v$RESOLVED_VERSION"
2
+ tag-template: "v$RESOLVED_VERSION"
3
+ categories:
4
+ - title: "New Features ✨"
5
+ labels:
6
+ - "feature"
7
+ - "enhancement"
8
+ - title: "Bug Fixes 🐛"
9
+ labels:
10
+ - "fix"
11
+ - "bugfix"
12
+ - "bug"
13
+ - title: "Under the Hood ⚙️"
14
+ labels:
15
+ - "chore"
16
+ - "ci"
17
+ - "refactor"
18
+ - "testing"
19
+ - title: "Documentation 📖"
20
+ label: "docs"
21
+ change-template: "- $TITLE (#$NUMBER)"
22
+ change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
23
+ version-resolver:
24
+ minor:
25
+ labels:
26
+ - "enhancement"
27
+ - "feature"
28
+ - "breaking"
29
+ # Semver rules are different before "1.0" If your project is v0.x, you
30
+ # don't want to auto-bump to 1.0.
31
+ # Uncomment and dedent if when/if you want to auto-select a major version
32
+ # bump.
33
+ # major:
34
+ # labels:
35
+ # - "breaking"
36
+ patch:
37
+ labels:
38
+ - "bug"
39
+ - "bugfix"
40
+ - "fix"
41
+ - "chore"
42
+ - "ci"
43
+ - "docs"
44
+ - "refactor"
45
+ - "testing"
46
+ - "dependencies"
47
+ - "security"
48
+ default: patch
49
+ template: |
50
+ $CHANGES
51
+ autolabeler:
52
+ - label: "chore"
53
+ title:
54
+ - '/^chore(\(.*\))?\:/i'
55
+ - label: "testing"
56
+ title:
57
+ - '/^tests(\(.*\))?\:/i'
58
+ - label: "ci"
59
+ title:
60
+ - '/^ci(\(.*\))?\:/i'
61
+ - label: "bug"
62
+ title:
63
+ - '/^fix(\(.*\))?\:/i'
64
+ - label: "enhancement"
65
+ title:
66
+ - '/^feat(\(.*\))?/i'
67
+ - label: "docs"
68
+ title:
69
+ - '/^docs(\(.*\))?\:/i'
70
+ - label: "security"
71
+ title:
72
+ - '/^security(\(.*\))?\:/i'
73
+ - '/^fix(\(security\))?\:/i'
74
+ - label: "dependencies"
75
+ title:
76
+ - '/^chore\(deps\)\:/i'
77
+ - '/^build\(deps\)\:/i'
78
+ - label: "breaking"
79
+ title:
80
+ # Match conventional commit breaking change syntax: feat!: or fix!: etc.
81
+ - '/^[a-z]+(\([^)]+\))?!:/i'
@@ -0,0 +1,37 @@
1
+ name: On-Demand Fix
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ pr:
7
+ description: "PR Number"
8
+ type: string
9
+ required: false
10
+ comment-id:
11
+ description: "Comment ID (Optional)"
12
+ type: string
13
+ required: false
14
+
15
+ permissions:
16
+ contents: write
17
+ pull-requests: write
18
+
19
+ jobs:
20
+ run-autofix-command:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Authenticate as GitHub App (if available)
24
+ uses: actions/create-github-app-token@v2
25
+ id: get-app-token
26
+ continue-on-error: true
27
+ with:
28
+ app-id: ${{ secrets.OCTAVIA_BOT_APP_ID }}
29
+ private-key: ${{ secrets.OCTAVIA_BOT_PRIVATE_KEY }}
30
+
31
+ - name: Run Poe Autofix Command
32
+ uses: aaronsteers/poe-command-processor@v1.3.3
33
+ with:
34
+ pr: ${{ github.event.inputs.pr }}
35
+ comment-id: ${{ github.event.inputs.comment-id }}
36
+ github-token: ${{ steps.get-app-token.outputs.token || secrets.GITHUB_TOKEN }}
37
+ command: autofix
@@ -0,0 +1,31 @@
1
+ name: Format Check
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request: {}
8
+
9
+ jobs:
10
+ ruff-format-check:
11
+ name: Ruff Format Check
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Checkout code
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v2
19
+ with:
20
+ version: "latest"
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.10"
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --extra dev
29
+
30
+ - name: Check code format
31
+ run: uv run ruff format --diff .
@@ -0,0 +1,54 @@
1
+ name: Lint Check
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request: {}
8
+
9
+ jobs:
10
+ ruff-lint-check:
11
+ name: Ruff Lint Check
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Checkout code
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v2
19
+ with:
20
+ version: "latest"
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.10"
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --extra dev
29
+
30
+ - name: Run lint check
31
+ run: uv run ruff check .
32
+
33
+ deptry-check:
34
+ name: Dependency Analysis
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - name: Checkout code
38
+ uses: actions/checkout@v4
39
+
40
+ - name: Install uv
41
+ uses: astral-sh/setup-uv@v2
42
+ with:
43
+ version: "latest"
44
+
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v5
47
+ with:
48
+ python-version: "3.10"
49
+
50
+ - name: Install dependencies
51
+ run: uv sync --extra dev
52
+
53
+ - name: Run dependency analysis
54
+ run: uv run deptry .
@@ -0,0 +1,37 @@
1
+ name: On-Demand Lock
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ pr:
7
+ description: "PR Number"
8
+ type: string
9
+ required: false
10
+ comment-id:
11
+ description: "Comment ID (Optional)"
12
+ type: string
13
+ required: false
14
+
15
+ permissions:
16
+ contents: write
17
+ pull-requests: write
18
+
19
+ jobs:
20
+ run-lock-command:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Authenticate as GitHub App (if available)
24
+ uses: actions/create-github-app-token@v2
25
+ id: get-app-token
26
+ continue-on-error: true
27
+ with:
28
+ app-id: ${{ secrets.OCTAVIA_BOT_APP_ID }}
29
+ private-key: ${{ secrets.OCTAVIA_BOT_PRIVATE_KEY }}
30
+
31
+ - name: Run Poe Lock Command
32
+ uses: aaronsteers/poe-command-processor@v1.3.3
33
+ with:
34
+ pr: ${{ github.event.inputs.pr }}
35
+ comment-id: ${{ github.event.inputs.comment-id }}
36
+ github-token: ${{ steps.get-app-token.outputs.token || secrets.GITHUB_TOKEN }}
37
+ command: lock
@@ -0,0 +1,34 @@
1
+ name: On-Demand Poe Task
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ comment-id:
7
+ description: "Optional comment-id of the slash command. Ignore if not applicable."
8
+ required: false
9
+ pr:
10
+ description: "PR Number"
11
+ required: false
12
+
13
+ permissions:
14
+ contents: write
15
+ pull-requests: write
16
+
17
+ jobs:
18
+ run-poe-command:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Authenticate as GitHub App (if available)
22
+ uses: actions/create-github-app-token@v2
23
+ id: get-app-token
24
+ continue-on-error: true
25
+ with:
26
+ app-id: ${{ secrets.OCTAVIA_BOT_APP_ID }}
27
+ private-key: ${{ secrets.OCTAVIA_BOT_PRIVATE_KEY }}
28
+
29
+ - name: Run Poe Slash Command Processor
30
+ uses: aaronsteers/poe-command-processor@v1.3.3
31
+ with:
32
+ pr: ${{ github.event.inputs.pr }}
33
+ comment-id: ${{ github.event.inputs.comment-id }}
34
+ github-token: ${{ steps.get-app-token.outputs.token || secrets.GITHUB_TOKEN }}
@@ -0,0 +1,23 @@
1
+ name: PR Welcome
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, reopened]
6
+
7
+ permissions:
8
+ contents: read
9
+ issues: write
10
+ pull-requests: write
11
+
12
+ jobs:
13
+ welcome:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout repository
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Create welcome comment
20
+ uses: peter-evans/create-or-update-comment@v5
21
+ with:
22
+ issue-number: ${{ github.event.pull_request.number }}
23
+ body-file: .github/pr-welcome-message.md
@@ -0,0 +1,117 @@
1
+ # Prerelease Command Workflow
2
+ #
3
+ # This workflow is triggered by the /prerelease slash command on a PR.
4
+ # It dispatches the publish workflow to build and publish a prerelease version.
5
+ #
6
+ # Usage: Comment `/prerelease` on a PR to trigger this workflow.
7
+ #
8
+ # Requirements:
9
+ # - PyPI Trusted Publisher configured for publish.yml (see publish.yml header for setup)
10
+
11
+ name: On-Demand Prerelease
12
+
13
+ on:
14
+ workflow_dispatch:
15
+ inputs:
16
+ pr:
17
+ description: "PR Number"
18
+ type: string
19
+ required: true
20
+ comment-id:
21
+ description: "Comment ID (Optional)"
22
+ type: string
23
+ required: false
24
+
25
+ permissions:
26
+ contents: read
27
+ pull-requests: write
28
+ issues: write
29
+ actions: write
30
+
31
+ jobs:
32
+ prerelease:
33
+ name: Trigger Prerelease Publish
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - name: Create URL to the run output
37
+ id: vars
38
+ run: echo "run-url=https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT
39
+
40
+ - name: Append comment with job run link
41
+ id: first-comment-action
42
+ if: github.event.inputs.comment-id
43
+ uses: peter-evans/create-or-update-comment@v4
44
+ with:
45
+ comment-id: ${{ github.event.inputs.comment-id }}
46
+ issue-number: ${{ github.event.inputs.pr }}
47
+ body: |
48
+ > **Prerelease Build Started**
49
+ >
50
+ > Building and publishing prerelease package from this PR...
51
+ > [Check job output.](${{ steps.vars.outputs.run-url }})
52
+
53
+ - name: Checkout to get latest tag
54
+ uses: actions/checkout@v4
55
+ with:
56
+ fetch-depth: 0
57
+
58
+ - name: Compute prerelease version
59
+ id: version
60
+ run: |
61
+ # Get the latest tag version (strip 'v' prefix if present)
62
+ LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
63
+ BASE_VERSION=${LATEST_TAG#v}
64
+ # Create a unique prerelease version using PR number and run ID
65
+ # Format: {base}.dev{pr_number}{run_id} (e.g., 0.2.0.dev26123456789)
66
+ PRERELEASE_VERSION="${BASE_VERSION}.dev${{ github.event.inputs.pr }}${{ github.run_id }}"
67
+ echo "version=$PRERELEASE_VERSION" >> $GITHUB_OUTPUT
68
+ echo "Computed prerelease version: $PRERELEASE_VERSION"
69
+
70
+ - name: Trigger publish workflow
71
+ id: trigger-publish
72
+ uses: the-actions-org/workflow-dispatch@v4
73
+ with:
74
+ workflow: publish.yml
75
+ token: ${{ secrets.GITHUB_TOKEN }}
76
+ # Use refs/pull/<PR>/head for fork compatibility
77
+ ref: refs/pull/${{ github.event.inputs.pr }}/head
78
+ wait-for-completion: true
79
+ wait-for-completion-timeout: 10m
80
+ display-workflow-run-url: false
81
+ inputs: >-
82
+ {
83
+ "version": "${{ steps.version.outputs.version }}",
84
+ "publish_to_pypi": "true"
85
+ }
86
+
87
+ - name: Append success comment
88
+ if: success() && github.event.inputs.comment-id
89
+ uses: peter-evans/create-or-update-comment@v4
90
+ with:
91
+ comment-id: ${{ steps.first-comment-action.outputs.comment-id }}
92
+ issue-number: ${{ github.event.inputs.pr }}
93
+ reactions: rocket
94
+ body: |
95
+ > **Prerelease Published to TestPyPI**
96
+ >
97
+ > Version: `${{ steps.version.outputs.version }}`
98
+ > [View publish workflow](${{ steps.trigger-publish.outputs.workflow-url }})
99
+ >
100
+ > Install and test with any of the following:
101
+ > ```bash
102
+ > uv tool install fastmcp-extensions==${{ steps.version.outputs.version }} --index-url https://test.pypi.org/simple/
103
+ > uv add fastmcp-extensions==${{ steps.version.outputs.version }} --index-url https://test.pypi.org/simple/
104
+ > ```
105
+
106
+ - name: Append failure comment
107
+ if: failure() && github.event.inputs.comment-id
108
+ uses: peter-evans/create-or-update-comment@v4
109
+ with:
110
+ comment-id: ${{ steps.first-comment-action.outputs.comment-id }}
111
+ issue-number: ${{ github.event.inputs.pr }}
112
+ reactions: confused
113
+ body: |
114
+ > **Prerelease Build/Publish Failed**
115
+ >
116
+ > The prerelease encountered an error.
117
+ > [Check job output](${{ steps.vars.outputs.run-url }}) for details.
@@ -0,0 +1,199 @@
1
+ # This workflow builds the python package and publishes to PyPI.
2
+ #
3
+ # Triggers:
4
+ # - release: published - When user clicks "Publish" on a draft release (downloads pre-built assets)
5
+ # - workflow_dispatch - Manual trigger for prereleases (builds fresh)
6
+ #
7
+ # Authentication: This workflow expects GitHub OIDC for passwordless PyPI publishing.
8
+ # For more info: https://docs.pypi.org/trusted-publishers/
9
+
10
+ name: Publish Package
11
+
12
+ on:
13
+ release:
14
+ types: [published]
15
+ workflow_dispatch:
16
+ inputs:
17
+ version:
18
+ description: >
19
+ Note that this workflow is intended for prereleases. For public-facing stable releases,
20
+ please use the GitHub Releases workflow instead.
21
+ For prereleases, please leave the version blank to use the detected version. Alternatively,
22
+ you can override the dynamic versioning for special use cases.
23
+ required: false
24
+ publish_to_pypi:
25
+ description: "Publish to PyPI. If true, the workflow will publish to PyPI."
26
+ type: boolean
27
+ required: true
28
+ default: true
29
+
30
+ jobs:
31
+ # Download pre-built assets from the release (only on release: published)
32
+ download_release_assets:
33
+ name: Download Release Assets
34
+ runs-on: ubuntu-latest
35
+ if: github.event_name == 'release'
36
+ steps:
37
+ - name: Download wheel and sdist from release
38
+ env:
39
+ GH_TOKEN: ${{ github.token }}
40
+ run: |
41
+ mkdir -p dist
42
+ gh release download "${{ github.event.release.tag_name }}" \
43
+ --repo "${{ github.repository }}" \
44
+ --pattern "*.whl" \
45
+ --pattern "*.tar.gz" \
46
+ --dir dist
47
+ echo "Downloaded assets:"
48
+ ls -la dist/
49
+
50
+ - name: Verify assets were downloaded
51
+ run: |
52
+ if [ ! -f dist/*.whl ]; then
53
+ echo "Error: No wheel file found in release assets"
54
+ exit 1
55
+ fi
56
+ if [ ! -f dist/*.tar.gz ]; then
57
+ echo "Error: No sdist file found in release assets"
58
+ exit 1
59
+ fi
60
+ echo "Assets verified successfully"
61
+
62
+ - uses: actions/upload-artifact@v5
63
+ with:
64
+ name: Packages-${{ github.run_id }}
65
+ path: |
66
+ dist/*.whl
67
+ dist/*.tar.gz
68
+
69
+ outputs:
70
+ VERSION: ${{ github.event.release.tag_name }}
71
+
72
+ # Build fresh package (only on workflow_dispatch, NOT on release)
73
+ build:
74
+ name: Build Python Package
75
+ runs-on: ubuntu-latest
76
+ if: github.event_name != 'release'
77
+ steps:
78
+ - name: Checkout Repo
79
+ uses: actions/checkout@v4
80
+ with:
81
+ fetch-depth: 0
82
+
83
+ - name: Detect Prerelease Version using Dunamai
84
+ uses: mtkennerly/dunamai-action@v1
85
+ with:
86
+ args: --format "{base}.post{distance}.dev${{ github.run_id }}"
87
+ env-var: DETECTED_VERSION
88
+
89
+ - name: Detect Release Tag Version from git ref ('${{ github.ref_name }}')
90
+ if: startsWith(github.ref, 'refs/tags/v')
91
+ run: |
92
+ echo "Overriding Dunamai detected version: '${{ env.DETECTED_VERSION || 'none' }}'"
93
+ # Extract the version from the git ref
94
+ DETECTED_VERSION=${{ github.ref_name }}
95
+ # Remove the 'v' prefix if it exists
96
+ DETECTED_VERSION="${DETECTED_VERSION#v}"
97
+ echo "Setting detected version to '$DETECTED_VERSION'"
98
+ echo "DETECTED_VERSION=${DETECTED_VERSION}" >> $GITHUB_ENV
99
+
100
+ - name: Validate and set VERSION (detected='${{ env.DETECTED_VERSION }}', input='${{ github.event.inputs.version || 'none' }}')
101
+ id: set_version
102
+ run: |
103
+ INPUT_VERSION=${{ github.event.inputs.version }}
104
+ echo "Version input set to '${INPUT_VERSION}'"
105
+ # Exit with success if both detected and input versions are empty
106
+ if [ -z "${DETECTED_VERSION:-}" ] && [ -z "${INPUT_VERSION:-}" ]; then
107
+ echo "No version detected or input. Will publish to SHA tag instead."
108
+ echo 'VERSION=' >> $GITHUB_ENV
109
+ exit 0
110
+ fi
111
+ # Remove the 'v' prefix if it exists
112
+ INPUT_VERSION="${INPUT_VERSION#v}"
113
+ # Fail if detected version is non-empty and different from the input version
114
+ if [ -n "${DETECTED_VERSION:-}" ] && [ -n "${INPUT_VERSION:-}" ] && [ "${DETECTED_VERSION}" != "${INPUT_VERSION}" ]; then
115
+ echo "Warning: Version input '${INPUT_VERSION}' does not match detected version '${DETECTED_VERSION}'."
116
+ echo "Using input version '${INPUT_VERSION}' instead."
117
+ fi
118
+ # Set the version to the input version if non-empty, otherwise the detected version
119
+ VERSION="${INPUT_VERSION:-$DETECTED_VERSION}"
120
+ # Fail if the version is still empty
121
+ if [ -z "$VERSION" ]; then
122
+ echo "Error: VERSION is not set. Ensure the tag follows the format 'refs/tags/vX.Y.Z'."
123
+ exit 1
124
+ fi
125
+ echo "Setting version to '$VERSION'"
126
+ echo "VERSION=${VERSION}" >> $GITHUB_ENV
127
+ echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
128
+ # Check if version is a prerelease version (will not tag 'latest')
129
+ if [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
130
+ echo "IS_PRERELEASE=false" >> $GITHUB_ENV
131
+ echo "IS_PRERELEASE=false" >> $GITHUB_OUTPUT
132
+ else
133
+ echo "IS_PRERELEASE=true" >> $GITHUB_ENV
134
+ echo "IS_PRERELEASE=true" >> $GITHUB_OUTPUT
135
+ fi
136
+
137
+ - name: Install uv
138
+ uses: astral-sh/setup-uv@v2
139
+ with:
140
+ version: "latest"
141
+
142
+ - name: Set up Python
143
+ uses: actions/setup-python@v5
144
+ with:
145
+ python-version: "3.12"
146
+
147
+ - name: Build package
148
+ env:
149
+ # Use UV_DYNAMIC_VERSIONING_BYPASS to override the version in pyproject.toml
150
+ # This ensures the build matches the detected/validated VERSION.
151
+ UV_DYNAMIC_VERSIONING_BYPASS: ${{ env.VERSION }}
152
+ run: uv build
153
+
154
+ - uses: actions/upload-artifact@v5
155
+ with:
156
+ name: Packages-${{ github.run_id }}
157
+ path: |
158
+ dist/*.whl
159
+ dist/*.tar.gz
160
+
161
+ outputs:
162
+ VERSION: ${{ steps.set_version.outputs.VERSION }}
163
+ IS_PRERELEASE: ${{ steps.set_version.outputs.IS_PRERELEASE }}
164
+
165
+ publish_to_pypi:
166
+ name: Publish Package to PyPI
167
+ runs-on: ubuntu-latest
168
+ # Depend on whichever job ran (build for dispatch, download for release)
169
+ needs: [build, download_release_assets]
170
+ # Always run if at least one of the needed jobs succeeded
171
+ if: |
172
+ always() &&
173
+ (needs.build.result == 'success' || needs.download_release_assets.result == 'success') &&
174
+ (
175
+ github.event_name == 'release' ||
176
+ (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_to_pypi == 'true')
177
+ )
178
+ permissions:
179
+ id-token: write
180
+ contents: write
181
+ environment:
182
+ name: PyPI
183
+ url: https://pypi.org/p/fastmcp-extensions/
184
+ env:
185
+ VERSION: ${{ needs.build.outputs.VERSION || needs.download_release_assets.outputs.VERSION }}
186
+ IS_PRERELEASE: ${{ needs.build.outputs.IS_PRERELEASE || 'false' }}
187
+ steps:
188
+ - uses: actions/download-artifact@v6
189
+ with:
190
+ name: Packages-${{ github.run_id }}
191
+ path: dist
192
+
193
+ # Note: Wheel upload to GitHub release is handled by release-drafter.yml
194
+ # during the draft stage (before publish). This avoids the "immutable release"
195
+ # error that occurs when trying to upload assets after a release is published.
196
+
197
+ - name: Publish to PyPI
198
+ # Uses GitHub OIDC for passwordless authentication (see header comment)
199
+ uses: pypa/gh-action-pypi-publish@v1.13.0