Add comprehensive release infrastructure and tooling
- Add automated GitHub Actions workflow for multi-platform releases - Add release script with version management and validation - Add Docker container support with multi-stage builds - Add comprehensive release documentation and templates - Update Cargo.toml with complete package metadata - Add command-line argument parsing with security options - Update README with detailed configuration examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(chmod:*)"
|
||||
"Bash(chmod:*)",
|
||||
"Bash(cargo build:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: Release Checklist
|
||||
about: Checklist for preparing a new release
|
||||
title: 'Release v[VERSION]'
|
||||
labels: 'release'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Pre-release Checklist
|
||||
|
||||
- [ ] All planned features and fixes are merged
|
||||
- [ ] All tests are passing on main branch
|
||||
- [ ] Documentation is updated
|
||||
- [ ] CHANGELOG.md is updated (if maintained separately)
|
||||
- [ ] Version is updated in Cargo.toml
|
||||
- [ ] No critical security vulnerabilities in dependencies
|
||||
|
||||
## Release Process
|
||||
|
||||
- [ ] Run `./scripts/release.sh [patch|minor|major|version X.Y.Z]`
|
||||
- [ ] Verify all CI checks pass
|
||||
- [ ] Tag is created and pushed
|
||||
- [ ] GitHub release is created automatically
|
||||
- [ ] Binaries are built for all platforms
|
||||
- [ ] Crate is published to crates.io (for stable releases)
|
||||
- [ ] Docker images are pushed
|
||||
|
||||
## Post-release Tasks
|
||||
|
||||
- [ ] Verify release artifacts are available
|
||||
- [ ] Test installation from released binaries
|
||||
- [ ] Update any dependent projects
|
||||
- [ ] Announce release (if applicable)
|
||||
|
||||
## Release Notes
|
||||
|
||||
<!--
|
||||
Add release notes here:
|
||||
- New features
|
||||
- Bug fixes
|
||||
- Breaking changes
|
||||
- Performance improvements
|
||||
- Security fixes
|
||||
-->
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
# Test the release script (dry run)
|
||||
./scripts/release.sh patch --dry-run
|
||||
|
||||
# Run pre-release checks
|
||||
./scripts/release.sh check
|
||||
|
||||
# Create the actual release
|
||||
./scripts/release.sh patch # or minor/major/version X.Y.Z
|
||||
```
|
||||
@@ -0,0 +1,490 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to release (e.g., v1.0.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
jobs:
|
||||
validate-release:
|
||||
name: Validate Release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.get-version.outputs.version }}
|
||||
tag: ${{ steps.get-version.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get version from tag or input
|
||||
id: get-version
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${VERSION}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=${VERSION#v}" >> $GITHUB_OUTPUT
|
||||
echo "tag=${VERSION}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Validate version format
|
||||
run: |
|
||||
VERSION="${{ steps.get-version.outputs.version }}"
|
||||
if ! [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "❌ Invalid version format: $VERSION"
|
||||
echo "Expected format: X.Y.Z or X.Y.Z-suffix"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Valid version format: $VERSION"
|
||||
|
||||
- name: Check if version matches Cargo.toml
|
||||
run: |
|
||||
CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
|
||||
INPUT_VERSION="${{ steps.get-version.outputs.version }}"
|
||||
if [ "$CARGO_VERSION" != "$INPUT_VERSION" ]; then
|
||||
echo "❌ Version mismatch:"
|
||||
echo " Cargo.toml: $CARGO_VERSION"
|
||||
echo " Tag/Input: $INPUT_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Version matches Cargo.toml: $INPUT_VERSION"
|
||||
|
||||
test:
|
||||
name: Run Tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: validate-release
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
rust: [stable]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
|
||||
- name: Cache Cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Install system dependencies (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libfontconfig1-dev \
|
||||
libfreetype6-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev
|
||||
|
||||
- name: Install system dependencies (macOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew update
|
||||
brew install pkg-config freetype jpeg libpng
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --all-features --verbose
|
||||
|
||||
build:
|
||||
name: Build Release Artifacts
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
needs: [validate-release, test]
|
||||
strategy:
|
||||
matrix:
|
||||
job:
|
||||
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, use-cross: false }
|
||||
- { os: ubuntu-latest, target: x86_64-unknown-linux-musl, use-cross: true }
|
||||
- { os: ubuntu-latest, target: aarch64-unknown-linux-gnu, use-cross: true }
|
||||
- { os: ubuntu-latest, target: aarch64-unknown-linux-musl, use-cross: true }
|
||||
- { os: windows-latest, target: x86_64-pc-windows-msvc, use-cross: false }
|
||||
- { os: macos-latest, target: x86_64-apple-darwin, use-cross: false }
|
||||
- { os: macos-latest, target: aarch64-apple-darwin, use-cross: false }
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.job.target }}
|
||||
|
||||
- name: Install cross compilation tools
|
||||
if: matrix.job.use-cross
|
||||
run: |
|
||||
cargo install cross --git https://github.com/cross-rs/cross
|
||||
|
||||
- name: Cache Cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-${{ matrix.job.target }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Install system dependencies (Ubuntu)
|
||||
if: matrix.job.os == 'ubuntu-latest' && !matrix.job.use-cross
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libfontconfig1-dev \
|
||||
libfreetype6-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev
|
||||
|
||||
- name: Install system dependencies (macOS)
|
||||
if: matrix.job.os == 'macos-latest'
|
||||
run: |
|
||||
brew update
|
||||
brew install pkg-config freetype jpeg libpng
|
||||
|
||||
- name: Build release binary
|
||||
run: |
|
||||
if [ "${{ matrix.job.use-cross }}" = "true" ]; then
|
||||
cross build --release --target ${{ matrix.job.target }} --all-features
|
||||
else
|
||||
cargo build --release --target ${{ matrix.job.target }} --all-features
|
||||
fi
|
||||
|
||||
- name: Prepare release archive
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION="${{ needs.validate-release.outputs.version }}"
|
||||
TARGET="${{ matrix.job.target }}"
|
||||
|
||||
# Create staging directory
|
||||
mkdir -p staging
|
||||
|
||||
# Copy binary
|
||||
if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
|
||||
cp "target/${TARGET}/release/docx-mcp.exe" staging/
|
||||
BINARY="docx-mcp.exe"
|
||||
else
|
||||
cp "target/${TARGET}/release/docx-mcp" staging/
|
||||
BINARY="docx-mcp"
|
||||
fi
|
||||
|
||||
# Copy additional files
|
||||
cp README.md staging/
|
||||
cp LICENSE staging/
|
||||
|
||||
# Create archive name
|
||||
ARCHIVE="docx-mcp-${VERSION}-${TARGET}"
|
||||
|
||||
# Create archive
|
||||
cd staging
|
||||
if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
|
||||
7z a "../${ARCHIVE}.zip" *
|
||||
echo "ARCHIVE=${ARCHIVE}.zip" >> $GITHUB_ENV
|
||||
else
|
||||
tar czf "../${ARCHIVE}.tar.gz" *
|
||||
echo "ARCHIVE=${ARCHIVE}.tar.gz" >> $GITHUB_ENV
|
||||
fi
|
||||
cd ..
|
||||
|
||||
# Generate checksums
|
||||
if [[ "${{ matrix.job.os }}" == "windows-latest" ]]; then
|
||||
certutil -hashfile "${ARCHIVE}.zip" SHA256 > "${ARCHIVE}.zip.sha256"
|
||||
else
|
||||
shasum -a 256 "${ARCHIVE}.tar.gz" > "${ARCHIVE}.tar.gz.sha256"
|
||||
fi
|
||||
|
||||
echo "BINARY=${BINARY}" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload release artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-${{ matrix.job.target }}
|
||||
path: |
|
||||
${{ env.ARCHIVE }}
|
||||
${{ env.ARCHIVE }}.sha256
|
||||
|
||||
create-release:
|
||||
name: Create GitHub Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-release, build]
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Prepare release assets
|
||||
run: |
|
||||
mkdir -p release-assets
|
||||
find artifacts -type f -name "*.tar.gz" -o -name "*.zip" -o -name "*.sha256" | \
|
||||
xargs -I {} cp {} release-assets/
|
||||
ls -la release-assets/
|
||||
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
run: |
|
||||
VERSION="${{ needs.validate-release.outputs.version }}"
|
||||
TAG="${{ needs.validate-release.outputs.tag }}"
|
||||
|
||||
# Get previous tag
|
||||
PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${TAG}$" | head -1)
|
||||
|
||||
echo "Generating changelog from ${PREV_TAG} to ${TAG}"
|
||||
|
||||
# Generate changelog
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges ${PREV_TAG}..HEAD)
|
||||
else
|
||||
CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges)
|
||||
fi
|
||||
|
||||
# Create release notes
|
||||
cat > release-notes.md << EOF
|
||||
## What's Changed
|
||||
|
||||
${CHANGELOG}
|
||||
|
||||
## Installation
|
||||
|
||||
### Pre-built Binaries
|
||||
|
||||
Download the appropriate binary for your system:
|
||||
|
||||
- **Linux x86_64**: \`docx-mcp-${VERSION}-x86_64-unknown-linux-gnu.tar.gz\`
|
||||
- **Linux x86_64 (musl)**: \`docx-mcp-${VERSION}-x86_64-unknown-linux-musl.tar.gz\`
|
||||
- **Linux ARM64**: \`docx-mcp-${VERSION}-aarch64-unknown-linux-gnu.tar.gz\`
|
||||
- **macOS Intel**: \`docx-mcp-${VERSION}-x86_64-apple-darwin.tar.gz\`
|
||||
- **macOS Apple Silicon**: \`docx-mcp-${VERSION}-aarch64-apple-darwin.tar.gz\`
|
||||
- **Windows x86_64**: \`docx-mcp-${VERSION}-x86_64-pc-windows-msvc.zip\`
|
||||
|
||||
### From Source
|
||||
|
||||
\`\`\`bash
|
||||
cargo install --git https://github.com/hongkongkiwi/docx-mcp --tag ${TAG}
|
||||
\`\`\`
|
||||
|
||||
### Verification
|
||||
|
||||
All binaries are provided with SHA256 checksums for verification:
|
||||
|
||||
\`\`\`bash
|
||||
# Linux/macOS
|
||||
shasum -a 256 -c docx-mcp-${VERSION}-your-target.tar.gz.sha256
|
||||
|
||||
# Windows
|
||||
certutil -hashfile docx-mcp-${VERSION}-x86_64-pc-windows-msvc.zip SHA256
|
||||
\`\`\`
|
||||
|
||||
## Full Changelog
|
||||
|
||||
**Full Changelog**: https://github.com/hongkongkiwi/docx-mcp/compare/${PREV_TAG}...${TAG}
|
||||
EOF
|
||||
|
||||
echo "CHANGELOG_FILE=release-notes.md" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.validate-release.outputs.tag }}
|
||||
name: Release ${{ needs.validate-release.outputs.tag }}
|
||||
body_path: ${{ steps.changelog.outputs.CHANGELOG_FILE }}
|
||||
files: release-assets/*
|
||||
draft: false
|
||||
prerelease: ${{ contains(needs.validate-release.outputs.version, '-') }}
|
||||
generate_release_notes: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish-crate:
|
||||
name: Publish to crates.io
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-release, create-release]
|
||||
if: ${{ !contains(needs.validate-release.outputs.version, '-') }} # Only publish stable releases
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libfontconfig1-dev \
|
||||
libfreetype6-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev
|
||||
|
||||
- name: Cache Cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ubuntu-cargo-publish-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Verify package
|
||||
run: cargo package --dry-run
|
||||
|
||||
- name: Publish to crates.io
|
||||
run: cargo publish
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
||||
docker-release:
|
||||
name: Build and Push Docker Images
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-release, create-release]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ github.repository }}
|
||||
${{ secrets.DOCKERHUB_USERNAME && format('{0}/docx-mcp', secrets.DOCKERHUB_USERNAME) || '' }}
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
update-docs:
|
||||
name: Update Documentation
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-release, create-release]
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libfontconfig1-dev \
|
||||
libfreetype6-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev
|
||||
|
||||
- name: Generate documentation
|
||||
run: |
|
||||
cargo doc --all-features --no-deps
|
||||
echo '<meta http-equiv="refresh" content="0; url=docx_mcp">' > target/doc/index.html
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./target/doc
|
||||
cname: docs.example.com # Replace with your custom domain if you have one
|
||||
|
||||
notify-success:
|
||||
name: Notify Release Success
|
||||
runs-on: ubuntu-latest
|
||||
needs: [create-release, publish-crate, docker-release, update-docs]
|
||||
if: success()
|
||||
steps:
|
||||
- name: Success notification
|
||||
run: |
|
||||
echo "🎉 Release ${{ needs.validate-release.outputs.tag }} completed successfully!"
|
||||
echo "- ✅ GitHub release created"
|
||||
echo "- ✅ Binaries built for all platforms"
|
||||
echo "- ✅ Published to crates.io"
|
||||
echo "- ✅ Docker images pushed"
|
||||
echo "- ✅ Documentation updated"
|
||||
|
||||
notify-failure:
|
||||
name: Notify Release Failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-release, test, build, create-release, publish-crate, docker-release, update-docs]
|
||||
if: failure()
|
||||
steps:
|
||||
- name: Failure notification
|
||||
run: |
|
||||
echo "❌ Release ${{ needs.validate-release.outputs.tag }} failed!"
|
||||
echo "Please check the workflow logs for details."
|
||||
exit 1
|
||||
+23
-3
@@ -2,11 +2,28 @@
|
||||
name = "docx-mcp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Your Name <your.email@example.com>"]
|
||||
description = "A comprehensive Model Context Protocol (MCP) server for Microsoft Word DOCX file manipulation"
|
||||
documentation = "https://docs.rs/docx-mcp"
|
||||
homepage = "https://github.com/hongkongkiwi/docx-mcp"
|
||||
repository = "https://github.com/hongkongkiwi/docx-mcp"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
keywords = ["mcp", "docx", "word", "document", "pdf"]
|
||||
categories = ["text-processing", "api-bindings", "command-line-utilities"]
|
||||
exclude = [
|
||||
"/.github/*",
|
||||
"/tests/fixtures/*",
|
||||
"/example/*",
|
||||
"/benches/*",
|
||||
"/.gitignore",
|
||||
"/deny.toml"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
# Official MCP SDK
|
||||
mcp-server = "0.3"
|
||||
mcp-core = "0.3"
|
||||
mcp-server = "0.1"
|
||||
mcp-core = "0.1"
|
||||
|
||||
# Async runtime
|
||||
tokio = { version = "1.40", features = ["full"] }
|
||||
@@ -26,7 +43,7 @@ lopdf = "0.34"
|
||||
rusttype = "0.9" # Font rendering in pure Rust
|
||||
|
||||
# Embedded fonts for PDF
|
||||
include_bytes_plus = "1.0"
|
||||
include-bytes-plus = "1.0"
|
||||
|
||||
# Image processing (pure Rust)
|
||||
image = { version = "0.25", features = ["png", "jpeg", "webp", "bmp", "gif"] }
|
||||
@@ -66,6 +83,9 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
regex = "1.10"
|
||||
once_cell = "1.20"
|
||||
|
||||
# Command line argument parsing
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
|
||||
# Optional external tool support
|
||||
headless_chrome = { version = "1.0", optional = true }
|
||||
wkhtmltopdf = { version = "0.4", optional = true }
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# Multi-stage Docker build for docx-mcp
|
||||
FROM rust:1.75-slim as builder
|
||||
|
||||
# Install system dependencies for building
|
||||
RUN apt-get update && apt-get install -y \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
libfontconfig1-dev \
|
||||
libfreetype6-dev \
|
||||
libjpeg-dev \
|
||||
libpng-dev \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy manifests
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY build.rs ./
|
||||
|
||||
# Copy source code
|
||||
COPY src/ ./src/
|
||||
COPY benches/ ./benches/
|
||||
COPY tests/ ./tests/
|
||||
|
||||
# Build the application
|
||||
RUN cargo build --release --all-features
|
||||
|
||||
# Runtime stage
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libssl3 \
|
||||
libfontconfig1 \
|
||||
libfreetype6 \
|
||||
libjpeg62-turbo \
|
||||
libpng16-16 \
|
||||
ca-certificates \
|
||||
libreoffice \
|
||||
poppler-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create non-root user
|
||||
RUN groupadd -r docxmcp && useradd -r -g docxmcp -s /bin/bash -d /app docxmcp
|
||||
|
||||
# Create app directory and set ownership
|
||||
WORKDIR /app
|
||||
RUN chown -R docxmcp:docxmcp /app
|
||||
|
||||
# Copy the built binary from builder stage
|
||||
COPY --from=builder /app/target/release/docx-mcp /usr/local/bin/docx-mcp
|
||||
RUN chmod +x /usr/local/bin/docx-mcp
|
||||
|
||||
# Copy additional files if needed
|
||||
COPY README.md LICENSE ./
|
||||
|
||||
# Switch to non-root user
|
||||
USER docxmcp
|
||||
|
||||
# Create temp directory for document processing
|
||||
RUN mkdir -p /tmp/docx-mcp && chmod 755 /tmp/docx-mcp
|
||||
|
||||
# Expose default MCP port (though MCP typically uses stdin/stdout)
|
||||
EXPOSE 8080
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD /usr/local/bin/docx-mcp --version || exit 1
|
||||
|
||||
# Set environment variables
|
||||
ENV RUST_LOG=info
|
||||
ENV DOCX_MCP_TEMP_DIR=/tmp/docx-mcp
|
||||
|
||||
# Default command
|
||||
CMD ["/usr/local/bin/docx-mcp"]
|
||||
@@ -60,8 +60,13 @@ The server includes comprehensive security features for enterprise and restricte
|
||||
### Readonly Mode
|
||||
```bash
|
||||
# Enable readonly mode - only allows document viewing and analysis
|
||||
|
||||
# Using environment variables
|
||||
export DOCX_MCP_READONLY=true
|
||||
./target/release/docx-mcp
|
||||
|
||||
# Using command line arguments
|
||||
./target/release/docx-mcp --readonly
|
||||
```
|
||||
|
||||
In readonly mode, only these operations are allowed:
|
||||
@@ -74,32 +79,51 @@ In readonly mode, only these operations are allowed:
|
||||
### Command Filtering
|
||||
```bash
|
||||
# Whitelist specific commands only
|
||||
|
||||
# Using environment variables
|
||||
export DOCX_MCP_WHITELIST="open_document,extract_text,get_metadata,export_to_markdown"
|
||||
|
||||
# Using command line arguments
|
||||
./target/release/docx-mcp --whitelist open_document,extract_text,get_metadata,export_to_markdown
|
||||
|
||||
# Or blacklist dangerous commands
|
||||
|
||||
# Using environment variables
|
||||
export DOCX_MCP_BLACKLIST="save_document,convert_to_pdf,merge_documents"
|
||||
|
||||
# Using command line arguments
|
||||
./target/release/docx-mcp --blacklist save_document,convert_to_pdf,merge_documents
|
||||
```
|
||||
|
||||
### Sandbox Mode
|
||||
```bash
|
||||
# Restrict all file operations to temp directory only
|
||||
|
||||
# Using environment variables
|
||||
export DOCX_MCP_SANDBOX=true
|
||||
./target/release/docx-mcp
|
||||
|
||||
# Using command line arguments
|
||||
./target/release/docx-mcp --sandbox
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
```bash
|
||||
# Set maximum document size (100MB default)
|
||||
|
||||
# Using environment variables
|
||||
export DOCX_MCP_MAX_SIZE=52428800 # 50MB
|
||||
|
||||
# Set maximum number of open documents
|
||||
export DOCX_MCP_MAX_DOCS=20
|
||||
|
||||
# Disable external tools
|
||||
export DOCX_MCP_NO_EXTERNAL_TOOLS=true
|
||||
|
||||
# Disable network operations
|
||||
export DOCX_MCP_NO_NETWORK=true
|
||||
./target/release/docx-mcp
|
||||
|
||||
# Using command line arguments
|
||||
./target/release/docx-mcp \
|
||||
--max-size 52428800 \
|
||||
--max-docs 20 \
|
||||
--no-external-tools \
|
||||
--no-network
|
||||
```
|
||||
|
||||
## 🤖 AI Tool Integration
|
||||
@@ -125,6 +149,39 @@ Add to your Claude Desktop configuration file:
|
||||
}
|
||||
```
|
||||
|
||||
**With Security Options (using command-line arguments):**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": ["--readonly", "--max-size", "52428800", "--no-network"],
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**With Security Options (using environment variables):**
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": [],
|
||||
"env": {
|
||||
"RUST_LOG": "info",
|
||||
"DOCX_MCP_READONLY": "true",
|
||||
"DOCX_MCP_MAX_SIZE": "52428800",
|
||||
"DOCX_MCP_NO_NETWORK": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After adding, restart Claude Desktop. You can then ask Claude to:
|
||||
- "Create a new Word document with our Q4 report"
|
||||
- "Convert this DOCX file to PDF"
|
||||
@@ -135,6 +192,7 @@ After adding, restart Claude Desktop. You can then ask Claude to:
|
||||
|
||||
Add to your Cursor settings (`~/.cursor/config.json` or through Settings UI):
|
||||
|
||||
**Basic Configuration:**
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
@@ -151,10 +209,24 @@ Add to your Cursor settings (`~/.cursor/config.json` or through Settings UI):
|
||||
}
|
||||
```
|
||||
|
||||
### Windsurf (Codeium)
|
||||
|
||||
Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
||||
**With Security Options (using command-line arguments):**
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": ["--sandbox", "--whitelist", "open_document,extract_text,export_to_markdown"],
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**With Security Options (using environment variables):**
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
@@ -162,6 +234,46 @@ Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": [],
|
||||
"env": {
|
||||
"RUST_LOG": "info",
|
||||
"DOCX_MCP_SANDBOX": "true",
|
||||
"DOCX_MCP_WHITELIST": "open_document,extract_text,export_to_markdown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Windsurf (Codeium)
|
||||
|
||||
Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
||||
|
||||
**Basic Configuration:**
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": [],
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**With Security Options (using arguments):**
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": ["--readonly", "--no-external-tools"],
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
}
|
||||
@@ -175,6 +287,7 @@ Add to your Windsurf configuration (`~/.windsurf/config.json`):
|
||||
|
||||
Add to your Continue configuration (`~/.continue/config.json`):
|
||||
|
||||
**Basic Configuration:**
|
||||
```json
|
||||
{
|
||||
"models": [
|
||||
@@ -192,10 +305,29 @@ Add to your Continue configuration (`~/.continue/config.json`):
|
||||
}
|
||||
```
|
||||
|
||||
**With Security Options:**
|
||||
```json
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"title": "Your Model",
|
||||
"provider": "your-provider",
|
||||
"mcp_servers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": ["--sandbox", "--max-size", "10485760"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### VS Code with MCP Extension
|
||||
|
||||
If using the MCP extension for VS Code, add to your workspace settings (`.vscode/settings.json`):
|
||||
|
||||
**Basic Configuration:**
|
||||
```json
|
||||
{
|
||||
"mcp.servers": {
|
||||
@@ -210,6 +342,67 @@ If using the MCP extension for VS Code, add to your workspace settings (`.vscode
|
||||
}
|
||||
```
|
||||
|
||||
**With Security Options:**
|
||||
```json
|
||||
{
|
||||
"mcp.servers": {
|
||||
"docx": {
|
||||
"command": "/absolute/path/to/docx-mcp/target/release/docx-mcp",
|
||||
"args": ["--readonly", "--blacklist", "save_document,merge_documents"],
|
||||
"env": {
|
||||
"RUST_LOG": "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Command Line Arguments
|
||||
|
||||
The DOCX MCP server supports the following command-line arguments for configuration:
|
||||
|
||||
```bash
|
||||
docx-mcp --help
|
||||
```
|
||||
|
||||
### Available Arguments
|
||||
|
||||
| Argument | Environment Variable | Description | Example |
|
||||
|----------|---------------------|-------------|---------|
|
||||
| `--readonly` | `DOCX_MCP_READONLY=true` | Enable readonly mode - only viewing operations | `--readonly` |
|
||||
| `--whitelist <COMMANDS>` | `DOCX_MCP_WHITELIST` | Comma-separated list of allowed commands | `--whitelist open_document,extract_text` |
|
||||
| `--blacklist <COMMANDS>` | `DOCX_MCP_BLACKLIST` | Comma-separated list of forbidden commands | `--blacklist save_document,convert_to_pdf` |
|
||||
| `--sandbox` | `DOCX_MCP_SANDBOX=true` | Restrict file operations to temp directory only | `--sandbox` |
|
||||
| `--no-external-tools` | `DOCX_MCP_NO_EXTERNAL_TOOLS=true` | Disable external tools (LibreOffice, etc.) | `--no-external-tools` |
|
||||
| `--no-network` | `DOCX_MCP_NO_NETWORK=true` | Disable network operations | `--no-network` |
|
||||
| `--max-size <BYTES>` | `DOCX_MCP_MAX_SIZE` | Maximum document size in bytes | `--max-size 52428800` |
|
||||
| `--max-docs <COUNT>` | `DOCX_MCP_MAX_DOCS` | Maximum number of open documents | `--max-docs 20` |
|
||||
| `--help` | - | Show help information | `--help` |
|
||||
| `--version` | - | Show version information | `--version` |
|
||||
|
||||
### Example Usage
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
./target/release/docx-mcp
|
||||
|
||||
# Readonly mode with size limit
|
||||
./target/release/docx-mcp --readonly --max-size 10485760
|
||||
|
||||
# Sandbox mode with command whitelist
|
||||
./target/release/docx-mcp --sandbox --whitelist open_document,extract_text,export_to_markdown
|
||||
|
||||
# Multiple security options
|
||||
./target/release/docx-mcp \
|
||||
--readonly \
|
||||
--no-external-tools \
|
||||
--no-network \
|
||||
--max-size 52428800 \
|
||||
--max-docs 10
|
||||
```
|
||||
|
||||
**Note:** Command-line arguments take precedence over environment variables when both are specified.
|
||||
|
||||
## 📚 Features
|
||||
|
||||
### Document Operations
|
||||
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
# Release Guide
|
||||
|
||||
This document describes the release process for docx-mcp.
|
||||
|
||||
## Overview
|
||||
|
||||
The release process is automated using GitHub Actions and includes:
|
||||
|
||||
- Automated testing on multiple platforms
|
||||
- Building release binaries for all supported targets
|
||||
- Publishing to crates.io
|
||||
- Creating GitHub releases with binaries
|
||||
- Building and pushing Docker images
|
||||
- Updating documentation
|
||||
|
||||
## Release Types
|
||||
|
||||
### Semantic Versioning
|
||||
|
||||
We follow [Semantic Versioning](https://semver.org/):
|
||||
|
||||
- **MAJOR**: Incompatible API changes
|
||||
- **MINOR**: New features (backwards compatible)
|
||||
- **PATCH**: Bug fixes (backwards compatible)
|
||||
|
||||
### Pre-release Versions
|
||||
|
||||
Pre-release versions can include suffixes like:
|
||||
- `1.0.0-alpha.1` - Alpha releases
|
||||
- `1.0.0-beta.1` - Beta releases
|
||||
- `1.0.0-rc.1` - Release candidates
|
||||
|
||||
## Quick Release Process
|
||||
|
||||
For most releases, use the automated release script:
|
||||
|
||||
```bash
|
||||
# Patch release (1.0.0 -> 1.0.1)
|
||||
./scripts/release.sh patch
|
||||
|
||||
# Minor release (1.0.0 -> 1.1.0)
|
||||
./scripts/release.sh minor
|
||||
|
||||
# Major release (1.0.0 -> 2.0.0)
|
||||
./scripts/release.sh major
|
||||
|
||||
# Specific version
|
||||
./scripts/release.sh version 1.5.0
|
||||
|
||||
# Pre-release
|
||||
./scripts/release.sh version 1.0.0-beta.1
|
||||
```
|
||||
|
||||
## Manual Release Process
|
||||
|
||||
If you need to create a release manually:
|
||||
|
||||
### 1. Pre-release Checks
|
||||
|
||||
```bash
|
||||
# Run all checks
|
||||
./scripts/release.sh check
|
||||
|
||||
# Or manually:
|
||||
cargo fmt --all -- --check
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
cargo test --all-features
|
||||
cargo build --release --all-features
|
||||
cargo package --dry-run
|
||||
```
|
||||
|
||||
### 2. Update Version
|
||||
|
||||
Update the version in `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
version = "1.2.3"
|
||||
```
|
||||
|
||||
Update `Cargo.lock`:
|
||||
|
||||
```bash
|
||||
cargo update -p docx-mcp
|
||||
```
|
||||
|
||||
### 3. Commit and Tag
|
||||
|
||||
```bash
|
||||
git add Cargo.toml Cargo.lock
|
||||
git commit -m "Release v1.2.3"
|
||||
git tag -a "v1.2.3" -m "Release v1.2.3"
|
||||
git push origin main
|
||||
git push origin v1.2.3
|
||||
```
|
||||
|
||||
### 4. GitHub Actions
|
||||
|
||||
The release workflow will automatically:
|
||||
|
||||
1. Validate the release
|
||||
2. Run tests on all platforms
|
||||
3. Build binaries for all targets
|
||||
4. Create GitHub release
|
||||
5. Publish to crates.io (stable releases only)
|
||||
6. Build and push Docker images
|
||||
7. Update documentation
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Release binaries are built for:
|
||||
|
||||
- **Linux**: x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl
|
||||
- **Linux ARM**: aarch64-unknown-linux-gnu, aarch64-unknown-linux-musl
|
||||
- **macOS**: x86_64-apple-darwin, aarch64-apple-darwin
|
||||
- **Windows**: x86_64-pc-windows-msvc
|
||||
|
||||
## Docker Images
|
||||
|
||||
Docker images are published to:
|
||||
|
||||
- GitHub Container Registry: `ghcr.io/hongkongkiwi/docx-mcp`
|
||||
- Docker Hub: `dockerhub-username/docx-mcp` (if configured)
|
||||
|
||||
Tags include:
|
||||
- `latest` - Latest stable release
|
||||
- `v1.2.3` - Specific version
|
||||
- `1.2.3` - Semantic version
|
||||
- `1.2` - Major.minor version
|
||||
- `1` - Major version
|
||||
|
||||
## Publishing to crates.io
|
||||
|
||||
Stable releases (without pre-release suffixes) are automatically published to crates.io.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Set `CARGO_REGISTRY_TOKEN` secret in GitHub repository settings
|
||||
2. Ensure you have publishing permissions for the crate
|
||||
|
||||
### Manual Publishing
|
||||
|
||||
```bash
|
||||
# Dry run
|
||||
cargo publish --dry-run
|
||||
|
||||
# Publish
|
||||
cargo publish
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Release Workflow Fails
|
||||
|
||||
1. Check the Actions tab in GitHub for detailed logs
|
||||
2. Common issues:
|
||||
- Version mismatch between tag and Cargo.toml
|
||||
- Tests failing on specific platforms
|
||||
- Missing secrets (CARGO_REGISTRY_TOKEN, DOCKERHUB credentials)
|
||||
|
||||
### Version Already Exists
|
||||
|
||||
If you need to recreate a release:
|
||||
|
||||
1. Delete the tag: `git tag -d v1.2.3 && git push origin :v1.2.3`
|
||||
2. Delete the GitHub release (if created)
|
||||
3. Create the tag again
|
||||
|
||||
### Docker Build Fails
|
||||
|
||||
1. Check if all dependencies are available in the Docker environment
|
||||
2. Verify Dockerfile syntax and build context
|
||||
3. Test locally: `docker build -t docx-mcp:test .`
|
||||
|
||||
### crates.io Publishing Fails
|
||||
|
||||
1. Verify `CARGO_REGISTRY_TOKEN` is set and valid
|
||||
2. Check if version already exists
|
||||
3. Ensure all required metadata is in Cargo.toml
|
||||
4. Run `cargo package --dry-run` to check for issues
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Signing Releases
|
||||
|
||||
Currently, releases are not cryptographically signed. Consider adding:
|
||||
|
||||
1. GPG signing of Git tags
|
||||
2. Binary signing with platform-specific tools
|
||||
3. SBOM (Software Bill of Materials) generation
|
||||
|
||||
### Supply Chain Security
|
||||
|
||||
- Dependencies are audited in CI with `cargo audit`
|
||||
- Docker images use specific base image versions
|
||||
- Build reproducibility is enhanced with Rust's deterministic builds
|
||||
|
||||
## Release Checklist
|
||||
|
||||
Use this checklist for important releases:
|
||||
|
||||
- [ ] All planned features are implemented
|
||||
- [ ] All tests pass locally and in CI
|
||||
- [ ] Documentation is updated
|
||||
- [ ] Breaking changes are documented
|
||||
- [ ] Migration guide is provided (for major releases)
|
||||
- [ ] Security implications are reviewed
|
||||
- [ ] Performance regression tests pass
|
||||
- [ ] Cross-platform compatibility verified
|
||||
- [ ] Release notes are prepared
|
||||
|
||||
## Post-Release Tasks
|
||||
|
||||
After a release:
|
||||
|
||||
1. **Verify Installation**: Test installation from released binaries
|
||||
2. **Update Examples**: Update example configurations if needed
|
||||
3. **Notify Users**: Announce significant releases
|
||||
4. **Monitor Issues**: Watch for issues related to the new release
|
||||
5. **Update Dependencies**: Consider updating dependent projects
|
||||
|
||||
## Emergency Releases
|
||||
|
||||
For critical security fixes:
|
||||
|
||||
1. Create a hotfix branch from the affected release tag
|
||||
2. Apply minimal fix
|
||||
3. Follow expedited release process
|
||||
4. Consider yanking affected versions from crates.io if necessary
|
||||
|
||||
```bash
|
||||
# Yank a version from crates.io (if needed)
|
||||
cargo yank --version 1.2.3
|
||||
|
||||
# Un-yank if needed later
|
||||
cargo yank --version 1.2.3 --undo
|
||||
```
|
||||
|
||||
## Release Schedule
|
||||
|
||||
- **Patch releases**: As needed for bug fixes
|
||||
- **Minor releases**: Monthly or when significant features accumulate
|
||||
- **Major releases**: Annually or when breaking changes are necessary
|
||||
|
||||
## Getting Help
|
||||
|
||||
- Open an issue for release-related problems
|
||||
- Check GitHub Actions logs for CI failures
|
||||
- Review this guide and workflow files for automation details
|
||||
@@ -169,20 +169,62 @@ flamegraph:
|
||||
changelog:
|
||||
git cliff --output CHANGELOG.md
|
||||
|
||||
# Prepare a release
|
||||
# Release commands using the release script
|
||||
|
||||
# Create a patch release (0.1.0 -> 0.1.1)
|
||||
release-patch:
|
||||
./scripts/release.sh patch
|
||||
|
||||
# Create a minor release (0.1.0 -> 0.2.0)
|
||||
release-minor:
|
||||
./scripts/release.sh minor
|
||||
|
||||
# Create a major release (0.1.0 -> 1.0.0)
|
||||
release-major:
|
||||
./scripts/release.sh major
|
||||
|
||||
# Create a specific version release
|
||||
release-version version:
|
||||
./scripts/release.sh version {{version}}
|
||||
|
||||
# Dry run of patch release (see what would happen)
|
||||
release-patch-dry:
|
||||
./scripts/release.sh patch --dry-run
|
||||
|
||||
# Dry run of minor release
|
||||
release-minor-dry:
|
||||
./scripts/release.sh minor --dry-run
|
||||
|
||||
# Dry run of major release
|
||||
release-major-dry:
|
||||
./scripts/release.sh major --dry-run
|
||||
|
||||
# Dry run of specific version release
|
||||
release-version-dry version:
|
||||
./scripts/release.sh version {{version}} --dry-run
|
||||
|
||||
# Run all pre-release checks
|
||||
release-check:
|
||||
./scripts/release.sh check
|
||||
|
||||
# Generate changelog since last tag
|
||||
release-changelog:
|
||||
./scripts/release.sh changelog
|
||||
|
||||
# Create git tag for current version
|
||||
release-tag:
|
||||
./scripts/release.sh tag
|
||||
|
||||
# Prepare a release (legacy command - use release-* commands above)
|
||||
prepare-release version:
|
||||
# Update version in Cargo.toml
|
||||
sed -i 's/^version = ".*"/version = "{{version}}"/' Cargo.toml
|
||||
# Update dependencies to use new version
|
||||
cargo update
|
||||
# Run full test suite
|
||||
just ci
|
||||
# Generate changelog
|
||||
just changelog
|
||||
# Commit changes
|
||||
git add .
|
||||
git commit -m "chore: prepare release {{version}}"
|
||||
git tag -a "v{{version}}" -m "Release {{version}}"
|
||||
@echo "⚠️ This command is deprecated. Use 'just release-version {{version}}' instead."
|
||||
@echo "The new release commands provide better automation and safety checks."
|
||||
@echo ""
|
||||
@echo "Available release commands:"
|
||||
@echo " just release-patch - Bump patch version"
|
||||
@echo " just release-minor - Bump minor version"
|
||||
@echo " just release-major - Bump major version"
|
||||
@echo " just release-version X.Y.Z - Set specific version"
|
||||
|
||||
# Show project statistics
|
||||
stats:
|
||||
@@ -221,4 +263,214 @@ init-hooks:
|
||||
# Remove git hooks
|
||||
remove-hooks:
|
||||
rm -f .git/hooks/pre-commit
|
||||
echo "Pre-commit hook removed"
|
||||
echo "Pre-commit hook removed"
|
||||
|
||||
# Docker commands
|
||||
|
||||
# Build multi-platform Docker image
|
||||
docker-build-multiarch:
|
||||
docker buildx create --use --name multiarch || true
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t docx-mcp:latest .
|
||||
|
||||
# Build and tag Docker image for release
|
||||
docker-build-release version:
|
||||
docker buildx build --platform linux/amd64,linux/arm64 \
|
||||
-t docx-mcp:{{version}} \
|
||||
-t docx-mcp:latest \
|
||||
-t ghcr.io/hongkongkiwi/docx-mcp:{{version}} \
|
||||
-t ghcr.io/hongkongkiwi/docx-mcp:latest \
|
||||
.
|
||||
|
||||
# Push Docker images to registry
|
||||
docker-push version:
|
||||
docker push docx-mcp:{{version}}
|
||||
docker push docx-mcp:latest
|
||||
docker push ghcr.io/hongkongkiwi/docx-mcp:{{version}}
|
||||
docker push ghcr.io/hongkongkiwi/docx-mcp:latest
|
||||
|
||||
# Run Docker container with volume mount for testing
|
||||
docker-test:
|
||||
docker run --rm -it -v $(pwd)/test-docs:/test-docs docx-mcp:latest
|
||||
|
||||
# Development environment commands
|
||||
|
||||
# Full development setup from scratch
|
||||
dev-setup:
|
||||
# Install Rust if not present
|
||||
@if ! command -v rustup >/dev/null 2>&1; then \
|
||||
echo "Installing Rust..."; \
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
|
||||
source ~/.cargo/env; \
|
||||
fi
|
||||
# Setup toolchain and tools
|
||||
just setup
|
||||
# Initialize git hooks
|
||||
just init-hooks
|
||||
# Build project
|
||||
just build
|
||||
echo "✅ Development environment ready!"
|
||||
|
||||
# Check system dependencies
|
||||
check-deps:
|
||||
@echo "=== System Dependencies Check ==="
|
||||
@echo "Checking required tools..."
|
||||
@command -v rustc >/dev/null && echo "✅ Rust compiler found" || echo "❌ Rust compiler not found"
|
||||
@command -v cargo >/dev/null && echo "✅ Cargo found" || echo "❌ Cargo not found"
|
||||
@command -v git >/dev/null && echo "✅ Git found" || echo "❌ Git not found"
|
||||
@command -v docker >/dev/null && echo "✅ Docker found" || echo "❌ Docker not found"
|
||||
@command -v just >/dev/null && echo "✅ Just found" || echo "❌ Just not found"
|
||||
@echo ""
|
||||
@echo "Optional tools:"
|
||||
@command -v libreoffice >/dev/null && echo "✅ LibreOffice found" || echo "⚠️ LibreOffice not found (optional)"
|
||||
@command -v pdftoppm >/dev/null && echo "✅ pdftoppm found" || echo "⚠️ pdftoppm not found (optional)"
|
||||
@command -v convert >/dev/null && echo "✅ ImageMagick convert found" || echo "⚠️ ImageMagick not found (optional)"
|
||||
|
||||
# Cross-compilation commands
|
||||
|
||||
# Build for all supported targets
|
||||
build-all-targets:
|
||||
# Install targets if not present
|
||||
rustup target add x86_64-unknown-linux-gnu
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
rustup target add aarch64-unknown-linux-gnu
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
rustup target add x86_64-pc-windows-msvc
|
||||
# Build for each target
|
||||
cargo build --release --target x86_64-unknown-linux-gnu --all-features
|
||||
cargo build --release --target x86_64-unknown-linux-musl --all-features
|
||||
cargo build --release --target x86_64-apple-darwin --all-features
|
||||
@echo "✅ Built for all available targets"
|
||||
|
||||
# Build using cross for Linux targets
|
||||
build-cross-linux:
|
||||
cargo install cross --git https://github.com/cross-rs/cross
|
||||
cross build --release --target x86_64-unknown-linux-gnu --all-features
|
||||
cross build --release --target x86_64-unknown-linux-musl --all-features
|
||||
cross build --release --target aarch64-unknown-linux-gnu --all-features
|
||||
cross build --release --target aarch64-unknown-linux-musl --all-features
|
||||
|
||||
# Maintenance commands
|
||||
|
||||
# Update all dependencies to latest versions
|
||||
update-deps:
|
||||
cargo update
|
||||
cargo outdated --depth 1
|
||||
|
||||
# Check for security vulnerabilities and update
|
||||
security-update:
|
||||
cargo audit fix
|
||||
cargo update
|
||||
|
||||
# Clean everything (including registry cache)
|
||||
clean-all:
|
||||
cargo clean
|
||||
rm -rf ~/.cargo/registry/cache
|
||||
rm -rf ~/.cargo/git/db
|
||||
docker system prune -f
|
||||
|
||||
# Backup project (excluding target and build artifacts)
|
||||
backup:
|
||||
#!/usr/bin/env bash
|
||||
BACKUP_NAME="docx-mcp-backup-$(date +%Y%m%d-%H%M%S)"
|
||||
tar czf "${BACKUP_NAME}.tar.gz" \
|
||||
--exclude='target' \
|
||||
--exclude='.git' \
|
||||
--exclude='*.log' \
|
||||
--exclude='*.tmp' \
|
||||
.
|
||||
echo "✅ Backup created: ${BACKUP_NAME}.tar.gz"
|
||||
|
||||
# Development workflows
|
||||
|
||||
# Quick development loop (format, build, test unit, lint)
|
||||
dev-loop:
|
||||
just fmt
|
||||
just build
|
||||
just test-unit
|
||||
just clippy
|
||||
|
||||
# Full quality check (everything CI runs)
|
||||
quality-check:
|
||||
just fmt-check
|
||||
just clippy
|
||||
just test
|
||||
just docs-check
|
||||
just audit
|
||||
just deny
|
||||
|
||||
# Continuous development with file watching
|
||||
dev-watch:
|
||||
cargo install cargo-watch
|
||||
cargo watch -w src -w tests -x "build" -x "test --lib"
|
||||
|
||||
# Performance analysis
|
||||
perf-analysis:
|
||||
# Build optimized release
|
||||
cargo build --release --all-features
|
||||
# Run criterion benchmarks
|
||||
cargo bench --all-features
|
||||
# Generate flamegraph if available
|
||||
@if command -v flamegraph >/dev/null 2>&1; then \
|
||||
echo "Generating flamegraph..."; \
|
||||
cargo flamegraph --bin docx-mcp -- --help; \
|
||||
fi
|
||||
|
||||
# MCP-specific commands
|
||||
|
||||
# Test MCP server functionality
|
||||
test-mcp:
|
||||
@echo "Testing MCP server..."
|
||||
# Build the server
|
||||
cargo build --release --all-features
|
||||
# Run basic functionality test
|
||||
python3 example/test_client.py || echo "❌ MCP test failed"
|
||||
|
||||
# Generate MCP documentation
|
||||
mcp-docs:
|
||||
@echo "Generating MCP server documentation..."
|
||||
cargo run --bin docx-mcp -- --help > docs/CLI_REFERENCE.md
|
||||
@echo "✅ CLI reference updated"
|
||||
|
||||
# Example commands
|
||||
|
||||
# Run all examples
|
||||
run-examples:
|
||||
@echo "Running all examples..."
|
||||
@if [ -f example/test_client.py ]; then python3 example/test_client.py; fi
|
||||
@if [ -f example/automation_example.py ]; then python3 example/automation_example.py; fi
|
||||
|
||||
# Generate test documents
|
||||
gen-test-docs:
|
||||
@echo "Generating test documents..."
|
||||
mkdir -p test-docs
|
||||
# You could add commands here to generate various test DOCX files
|
||||
|
||||
# Utility commands
|
||||
|
||||
# Show detailed project info
|
||||
info:
|
||||
@echo "=== Project Information ==="
|
||||
@echo "Name: docx-mcp"
|
||||
@echo "Version: $(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')"
|
||||
@echo "Rust version: $(rustc --version)"
|
||||
@echo "Cargo version: $(cargo --version)"
|
||||
@echo ""
|
||||
just stats
|
||||
|
||||
# List all available commands with descriptions
|
||||
help:
|
||||
@echo "=== Available Commands ==="
|
||||
@just --list
|
||||
@echo ""
|
||||
@echo "=== Release Commands ==="
|
||||
@echo " release-patch - Create patch release (0.1.0 -> 0.1.1)"
|
||||
@echo " release-minor - Create minor release (0.1.0 -> 0.2.0)"
|
||||
@echo " release-major - Create major release (0.1.0 -> 1.0.0)"
|
||||
@echo " release-version X - Create specific version release"
|
||||
@echo " release-*-dry - Dry run versions of above commands"
|
||||
@echo ""
|
||||
@echo "=== Development Workflows ==="
|
||||
@echo " dev-loop - Quick development cycle"
|
||||
@echo " quality-check - Full quality assessment"
|
||||
@echo " dev-setup - Complete development environment setup"
|
||||
Executable
+355
@@ -0,0 +1,355 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Release script for docx-mcp
|
||||
# This script helps with version management and release preparation
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Helper functions
|
||||
info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# Check if we're in a git repository
|
||||
check_git_repo() {
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
error "Not in a git repository"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if working directory is clean
|
||||
check_clean_working_dir() {
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
error "Working directory is not clean. Please commit or stash your changes."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Get current version from Cargo.toml
|
||||
get_current_version() {
|
||||
grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/'
|
||||
}
|
||||
|
||||
# Update version in Cargo.toml
|
||||
update_version() {
|
||||
local new_version=$1
|
||||
info "Updating version to $new_version"
|
||||
|
||||
# Update Cargo.toml
|
||||
sed -i.bak "s/^version = \".*\"/version = \"$new_version\"/" Cargo.toml
|
||||
rm Cargo.toml.bak
|
||||
|
||||
# Update Cargo.lock
|
||||
cargo update -p docx-mcp
|
||||
|
||||
success "Version updated to $new_version"
|
||||
}
|
||||
|
||||
# Generate changelog since last tag
|
||||
generate_changelog() {
|
||||
local last_tag=$(git tag --sort=-version:refname | head -1)
|
||||
local new_version=$1
|
||||
|
||||
info "Generating changelog since $last_tag"
|
||||
|
||||
if [ -n "$last_tag" ]; then
|
||||
git log --pretty=format:"- %s (%h)" --no-merges ${last_tag}..HEAD > CHANGELOG.tmp
|
||||
else
|
||||
git log --pretty=format:"- %s (%h)" --no-merges > CHANGELOG.tmp
|
||||
fi
|
||||
|
||||
echo "## Release $new_version ($(date +%Y-%m-%d))"
|
||||
echo ""
|
||||
cat CHANGELOG.tmp
|
||||
echo ""
|
||||
rm CHANGELOG.tmp
|
||||
}
|
||||
|
||||
# Run pre-release checks
|
||||
run_checks() {
|
||||
info "Running pre-release checks..."
|
||||
|
||||
# Format check
|
||||
info "Checking code formatting..."
|
||||
cargo fmt --all -- --check
|
||||
success "Code formatting is correct"
|
||||
|
||||
# Clippy check
|
||||
info "Running Clippy..."
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
success "Clippy checks passed"
|
||||
|
||||
# Tests
|
||||
info "Running tests..."
|
||||
cargo test --all-features
|
||||
success "All tests passed"
|
||||
|
||||
# Build check
|
||||
info "Testing release build..."
|
||||
cargo build --release --all-features
|
||||
success "Release build successful"
|
||||
|
||||
# Package check
|
||||
info "Testing package..."
|
||||
cargo package --dry-run
|
||||
success "Package validation passed"
|
||||
}
|
||||
|
||||
# Create and push git tag
|
||||
create_tag() {
|
||||
local version=$1
|
||||
local tag="v$version"
|
||||
|
||||
info "Creating git tag $tag"
|
||||
|
||||
# Create annotated tag
|
||||
git tag -a "$tag" -m "Release $tag"
|
||||
|
||||
success "Created tag $tag"
|
||||
|
||||
# Ask if user wants to push
|
||||
echo -n "Push tag to origin? [y/N]: "
|
||||
read -r response
|
||||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||
git push origin "$tag"
|
||||
success "Tag pushed to origin"
|
||||
else
|
||||
warning "Tag not pushed. Remember to push it manually: git push origin $tag"
|
||||
fi
|
||||
}
|
||||
|
||||
# Show usage information
|
||||
usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [COMMAND] [OPTIONS]
|
||||
|
||||
Commands:
|
||||
patch Bump patch version (0.1.0 -> 0.1.1)
|
||||
minor Bump minor version (0.1.0 -> 0.2.0)
|
||||
major Bump major version (0.1.0 -> 1.0.0)
|
||||
version X.Y.Z Set specific version
|
||||
check Run pre-release checks only
|
||||
changelog Generate changelog since last tag
|
||||
tag Create git tag for current version
|
||||
|
||||
Options:
|
||||
--dry-run Show what would be done without making changes
|
||||
--no-checks Skip pre-release checks (not recommended)
|
||||
--no-tag Don't create git tag
|
||||
--help Show this help message
|
||||
|
||||
Examples:
|
||||
$0 patch # Bump to next patch version
|
||||
$0 version 1.0.0 # Set version to 1.0.0
|
||||
$0 check # Run all pre-release checks
|
||||
$0 patch --dry-run # Show what patch release would do
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse version bump type
|
||||
bump_version() {
|
||||
local current_version=$1
|
||||
local bump_type=$2
|
||||
|
||||
# Split version into components
|
||||
IFS='.' read -ra VERSION_PARTS <<< "$current_version"
|
||||
local major=${VERSION_PARTS[0]}
|
||||
local minor=${VERSION_PARTS[1]}
|
||||
local patch=${VERSION_PARTS[2]}
|
||||
|
||||
case $bump_type in
|
||||
"patch")
|
||||
patch=$((patch + 1))
|
||||
;;
|
||||
"minor")
|
||||
minor=$((minor + 1))
|
||||
patch=0
|
||||
;;
|
||||
"major")
|
||||
major=$((major + 1))
|
||||
minor=0
|
||||
patch=0
|
||||
;;
|
||||
*)
|
||||
error "Invalid bump type: $bump_type"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "${major}.${minor}.${patch}"
|
||||
}
|
||||
|
||||
# Validate version format
|
||||
validate_version() {
|
||||
local version=$1
|
||||
if ! [[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
error "Invalid version format: $version"
|
||||
error "Expected format: X.Y.Z or X.Y.Z-suffix"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
main() {
|
||||
local command=$1
|
||||
local dry_run=false
|
||||
local no_checks=false
|
||||
local no_tag=false
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run)
|
||||
dry_run=true
|
||||
shift
|
||||
;;
|
||||
--no-checks)
|
||||
no_checks=true
|
||||
shift
|
||||
;;
|
||||
--no-tag)
|
||||
no_tag=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
if [ -z "$command" ]; then
|
||||
command=$1
|
||||
elif [ -z "$version_arg" ] && [ "$command" = "version" ]; then
|
||||
version_arg=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if command provided
|
||||
if [ -z "$command" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Basic checks
|
||||
check_git_repo
|
||||
|
||||
if [ "$dry_run" = false ]; then
|
||||
check_clean_working_dir
|
||||
fi
|
||||
|
||||
current_version=$(get_current_version)
|
||||
info "Current version: $current_version"
|
||||
|
||||
case $command in
|
||||
"patch"|"minor"|"major")
|
||||
new_version=$(bump_version "$current_version" "$command")
|
||||
;;
|
||||
"version")
|
||||
if [ -z "$version_arg" ]; then
|
||||
error "Version argument required for 'version' command"
|
||||
exit 1
|
||||
fi
|
||||
new_version=$version_arg
|
||||
validate_version "$new_version"
|
||||
;;
|
||||
"check")
|
||||
run_checks
|
||||
success "All pre-release checks passed!"
|
||||
exit 0
|
||||
;;
|
||||
"changelog")
|
||||
generate_changelog "$current_version"
|
||||
exit 0
|
||||
;;
|
||||
"tag")
|
||||
if [ "$dry_run" = true ]; then
|
||||
info "Would create tag v$current_version"
|
||||
else
|
||||
create_tag "$current_version"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
error "Unknown command: $command"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
info "New version will be: $new_version"
|
||||
|
||||
if [ "$dry_run" = true ]; then
|
||||
warning "DRY RUN MODE - No changes will be made"
|
||||
info "Would update version from $current_version to $new_version"
|
||||
if [ "$no_checks" = false ]; then
|
||||
info "Would run pre-release checks"
|
||||
fi
|
||||
if [ "$no_tag" = false ]; then
|
||||
info "Would create git tag v$new_version"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Confirm with user
|
||||
echo -n "Proceed with release $new_version? [y/N]: "
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
warning "Release cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Run pre-release checks
|
||||
if [ "$no_checks" = false ]; then
|
||||
run_checks
|
||||
fi
|
||||
|
||||
# Update version
|
||||
update_version "$new_version"
|
||||
|
||||
# Commit version bump
|
||||
git add Cargo.toml Cargo.lock
|
||||
git commit -m "Release $new_version"
|
||||
success "Version bump committed"
|
||||
|
||||
# Create tag
|
||||
if [ "$no_tag" = false ]; then
|
||||
create_tag "$new_version"
|
||||
fi
|
||||
|
||||
# Generate changelog for reference
|
||||
info "Changelog for release:"
|
||||
generate_changelog "$new_version"
|
||||
|
||||
success "Release $new_version completed!"
|
||||
info "Next steps:"
|
||||
info "1. Push commits: git push origin main"
|
||||
if [ "$no_tag" = false ]; then
|
||||
info "2. Push tag: git push origin v$new_version (if not done already)"
|
||||
fi
|
||||
info "3. GitHub Actions will automatically create the release"
|
||||
}
|
||||
|
||||
# Run main function with all arguments
|
||||
main "$@"
|
||||
+4
-2
@@ -3,6 +3,7 @@ use mcp_server::{Server, ServerBuilder, ServerOptions};
|
||||
use mcp_core::ToolManager;
|
||||
use tracing::info;
|
||||
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
|
||||
use clap::Parser;
|
||||
|
||||
mod docx_tools;
|
||||
mod docx_handler;
|
||||
@@ -23,8 +24,9 @@ async fn main() -> Result<()> {
|
||||
.with(EnvFilter::from_default_env())
|
||||
.init();
|
||||
|
||||
// Load security configuration from environment
|
||||
let security_config = security::SecurityConfig::from_env();
|
||||
// Parse command line arguments (which also includes environment variables)
|
||||
let args = security::Args::parse();
|
||||
let security_config = security::SecurityConfig::from_args(args);
|
||||
info!("Starting DOCX MCP Server - Security: {}", security_config.get_summary());
|
||||
|
||||
let docx_provider = DocxToolsProvider::new_with_security(security_config);
|
||||
|
||||
+93
-1
@@ -2,6 +2,46 @@ use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use tracing::{debug, info, warn};
|
||||
use clap::Parser;
|
||||
|
||||
/// Command line arguments for the DOCX MCP server
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "docx-mcp")]
|
||||
#[command(about = "A comprehensive Model Context Protocol (MCP) server for Microsoft Word DOCX file manipulation")]
|
||||
#[command(version)]
|
||||
pub struct Args {
|
||||
/// Enable readonly mode - only allow viewing operations
|
||||
#[arg(long, env = "DOCX_MCP_READONLY")]
|
||||
pub readonly: bool,
|
||||
|
||||
/// Comma-separated whitelist of allowed commands
|
||||
#[arg(long, env = "DOCX_MCP_WHITELIST", value_delimiter = ',')]
|
||||
pub whitelist: Option<Vec<String>>,
|
||||
|
||||
/// Comma-separated blacklist of forbidden commands
|
||||
#[arg(long, env = "DOCX_MCP_BLACKLIST", value_delimiter = ',')]
|
||||
pub blacklist: Option<Vec<String>>,
|
||||
|
||||
/// Enable sandbox mode - restrict file operations to temp directory only
|
||||
#[arg(long, env = "DOCX_MCP_SANDBOX")]
|
||||
pub sandbox: bool,
|
||||
|
||||
/// Disable external tools (LibreOffice, etc.)
|
||||
#[arg(long, env = "DOCX_MCP_NO_EXTERNAL_TOOLS")]
|
||||
pub no_external_tools: bool,
|
||||
|
||||
/// Disable network operations
|
||||
#[arg(long, env = "DOCX_MCP_NO_NETWORK")]
|
||||
pub no_network: bool,
|
||||
|
||||
/// Maximum document size in bytes
|
||||
#[arg(long, env = "DOCX_MCP_MAX_SIZE")]
|
||||
pub max_size: Option<usize>,
|
||||
|
||||
/// Maximum number of open documents
|
||||
#[arg(long, env = "DOCX_MCP_MAX_DOCS")]
|
||||
pub max_docs: Option<usize>,
|
||||
}
|
||||
|
||||
/// Security configuration for the MCP server
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -47,7 +87,59 @@ impl Default for SecurityConfig {
|
||||
}
|
||||
|
||||
impl SecurityConfig {
|
||||
/// Load configuration from environment variables
|
||||
/// Create configuration from command line arguments
|
||||
pub fn from_args(args: Args) -> Self {
|
||||
let mut config = Self::default();
|
||||
|
||||
// Apply command line arguments
|
||||
if args.readonly {
|
||||
config.readonly_mode = true;
|
||||
info!("Running in READONLY mode - only viewing operations allowed");
|
||||
}
|
||||
|
||||
if let Some(whitelist) = args.whitelist {
|
||||
let commands: HashSet<String> = whitelist.into_iter().collect();
|
||||
info!("Command whitelist enabled with {} commands", commands.len());
|
||||
config.command_whitelist = Some(commands);
|
||||
}
|
||||
|
||||
if let Some(blacklist) = args.blacklist {
|
||||
let commands: HashSet<String> = blacklist.into_iter().collect();
|
||||
info!("Command blacklist enabled with {} blocked commands", commands.len());
|
||||
config.command_blacklist = Some(commands);
|
||||
}
|
||||
|
||||
if args.sandbox {
|
||||
config.sandbox_mode = true;
|
||||
config.allow_external_tools = false;
|
||||
config.allow_network = false;
|
||||
info!("Running in SANDBOX mode - restricted file operations");
|
||||
}
|
||||
|
||||
if args.no_external_tools {
|
||||
config.allow_external_tools = false;
|
||||
info!("External tools disabled");
|
||||
}
|
||||
|
||||
if args.no_network {
|
||||
config.allow_network = false;
|
||||
info!("Network operations disabled");
|
||||
}
|
||||
|
||||
if let Some(size) = args.max_size {
|
||||
config.max_document_size = size;
|
||||
info!("Max document size set to {} bytes", size);
|
||||
}
|
||||
|
||||
if let Some(max) = args.max_docs {
|
||||
config.max_open_documents = max;
|
||||
info!("Max open documents set to {}", max);
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
/// Load configuration from environment variables (deprecated, use from_args instead)
|
||||
pub fn from_env() -> Self {
|
||||
let mut config = Self::default();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user