We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/adambdooley/foundry-vtt-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
name: Build Complete Release
on:
push:
tags:
- 'v*'
branches:
- feature/nsis-installer
pull_request:
branches:
- master
workflow_dispatch:
inputs:
version:
description: 'Version tag (leave empty to auto-detect from package.json)'
required: false
default: ''
dry_run_foundry:
description: 'Test Foundry API call without actually updating registry'
required: false
default: 'false'
type: boolean
jobs:
build-nsis:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.12.2'
- name: Set Package Version
id: version
run: |
try {
if ("${{ github.event.inputs.version }}" -ne "") {
$version = "${{ github.event.inputs.version }}"
Write-Host "β Using manual input version: $version"
# Validate version format
if ($version -notmatch '^v?\d+\.\d+\.\d+') {
throw "Invalid version format: '$version'. Expected format: v0.0.0 or 0.0.0"
}
} elseif ("${{ github.ref_type }}" -eq "tag") {
$version = "${{ github.ref_name }}"
Write-Host "β Using tag version: $version"
# Validate tag version format
if ($version -notmatch '^v?\d+\.\d+\.\d+') {
throw "Invalid tag version format: '$version'. Expected format: v0.0.0"
}
} else {
Write-Host "π Reading version from package.json..."
# Check if package.json exists
if (-not (Test-Path "package.json")) {
throw "package.json not found in repository root"
}
# Read and parse package.json
$packageJson = Get-Content package.json -Raw | ConvertFrom-Json
# Validate version field exists
if (-not $packageJson.version) {
throw "No 'version' field found in package.json"
}
$packageVersion = $packageJson.version
Write-Host "π¦ Found package.json version: $packageVersion"
# Validate version format
if ($packageVersion -notmatch '^\d+\.\d+\.\d+') {
throw "Invalid package.json version format: '$packageVersion'. Expected format: 0.0.0"
}
$version = "v$packageVersion"
Write-Host "β Using package.json version: $version"
}
# Final validation - ensure no invalid characters for filename
if ($version -match '[<>:"/\\|?*]') {
throw "Version contains invalid filename characters: '$version'"
}
Write-Host "π― Final PACKAGE_VERSION: $version"
echo "PACKAGE_VERSION=$version" >> $env:GITHUB_ENV
echo "version=$version" >> $env:GITHUB_OUTPUT
} catch {
Write-Error "β Failed to determine package version: $($_.Exception.Message)"
Write-Error "π Context: github.event.inputs.version='${{ github.event.inputs.version }}', github.ref_type='${{ github.ref_type }}', github.ref_name='${{ github.ref_name }}'"
exit 1
}
- name: Install workspace dependencies (CI)
run: npm ci --workspaces --include-workspace-root
- name: Build shared types
run: npm run build --workspace=shared
- name: Build MCP Server (Bundled)
run: npm run build:bundle --workspace=packages/mcp-server
- name: Setup NSIS
uses: joncloud/makensis-action@v4.1
- name: Build NSIS Installer
run: |
cd installer
node build-nsis.js --version $env:PACKAGE_VERSION
- name: Create Foundry Module ZIP
run: |
Write-Host "π¦ Creating Foundry module ZIP for registry..."
cd packages/foundry-module
# Create ZIP excluding hidden files and node_modules
$compress = @{
Path = Get-ChildItem -Path "." -Exclude ".*", "node_modules"
DestinationPath = "..\..\foundry-vtt-mcp.zip"
CompressionLevel = "Optimal"
}
Compress-Archive @compress -Force
# Verify ZIP was created
if (Test-Path "..\..\foundry-vtt-mcp.zip") {
$size = (Get-Item "..\..\foundry-vtt-mcp.zip").Length / 1KB
Write-Host "β Foundry module ZIP created ($([math]::Round($size, 1)) KB)"
} else {
Write-Host "β Foundry module ZIP creation failed"
exit 1
}
- name: Create Standalone MCP Server ZIP
run: |
Write-Host "π¦ Creating standalone MCP server ZIP for manual installation..."
# Create a temporary directory for the standalone package
$standalonePath = "standalone-mcp-server"
New-Item -ItemType Directory -Force -Path $standalonePath
# Copy MCP server bundle
Copy-Item "packages\mcp-server\dist\index.bundle.cjs" "$standalonePath\index.js"
# Create package.json for standalone
$standalonePackage = @{
name = "foundry-mcp-server"
version = "${{ env.PACKAGE_VERSION }}".TrimStart('v')
description = "Foundry VTT MCP Server - Standalone Installation"
main = "index.js"
scripts = @{
start = "node index.js"
}
engines = @{
node = ">=18.0.0"
}
} | ConvertTo-Json -Depth 3
$standalonePackage | Out-File -FilePath "$standalonePath\package.json" -Encoding UTF8
# Create README for standalone
$readmeLines = @(
"# Foundry MCP Server - Manual Installation",
"",
"This package contains the standalone MCP server for manual installation on any platform.",
"",
"## Requirements",
"- Node.js 18.0.0 or later",
"- Foundry VTT with MCP Bridge module installed",
"",
"## Installation",
"1. Extract this ZIP to your desired location",
"2. Run: npm start or node index.js",
"3. Configure Claude Desktop to connect to this server",
"4. Install the MCP Bridge module in Foundry VTT",
"",
"For full documentation visit: https://github.com/adambdooley/foundry-vtt-mcp",
"",
"Version: ${{ env.PACKAGE_VERSION }}"
)
$readmeLines | Out-File -FilePath "$standalonePath\README.md" -Encoding UTF8
# Create the ZIP
$compress = @{
Path = $standalonePath
DestinationPath = "foundry-mcp-server-${{ env.PACKAGE_VERSION }}.zip"
CompressionLevel = "Optimal"
}
Compress-Archive @compress -Force
# Verify and clean up
if (Test-Path "foundry-mcp-server-${{ env.PACKAGE_VERSION }}.zip") {
$size = (Get-Item "foundry-mcp-server-${{ env.PACKAGE_VERSION }}.zip").Length / 1KB
Write-Host "β Standalone MCP server ZIP created ($([math]::Round($size, 1)) KB)"
} else {
Write-Host "β Standalone MCP server ZIP creation failed"
exit 1
}
# Clean up temporary directory
Remove-Item -Recurse -Force $standalonePath
- name: Verify installer package
run: |
cd installer/build
dir
if (Test-Path "FoundryMCPServer-Setup-$env:PACKAGE_VERSION.exe") {
Write-Host "β NSIS installer found"
$size = (Get-Item "FoundryMCPServer-Setup-$env:PACKAGE_VERSION.exe").Length / 1MB
Write-Host "π Installer size: $([math]::Round($size, 1)) MB"
} else {
Write-Host "β NSIS installer not found"
exit 1
}
- name: Upload installer as artifact
uses: actions/upload-artifact@v4
with:
name: foundry-mcp-server-installer-${{ env.PACKAGE_VERSION }}
path: installer/build/FoundryMCPServer-Setup-${{ env.PACKAGE_VERSION }}.exe
retention-days: 30
- name: Create Release with All Assets (on tag)
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: Foundry MCP Server ${{ github.ref_name }}
body: |
## Foundry MCP Server ${{ github.ref_name }}
Complete multi-platform distribution for Foundry MCP Server - AI-powered campaign management for Foundry VTT using Claude Desktop.
### π¦ Installation Options
**Windows Users (Recommended):**
- Download `FoundryMCPServer-Setup-${{ github.ref_name }}.exe`
- Run installer (no admin rights required)
- Automatic Claude Desktop configuration
**Manual Installation (All Platforms):**
- Download `foundry-mcp-server-${{ github.ref_name }}.zip`
- Extract and run with Node.js 18+
- Manual Claude Desktop configuration required
**Foundry VTT Users:**
- Install directly from Foundry's module browser
- Or use manifest: `https://raw.githubusercontent.com/adambdooley/foundry-vtt-mcp/master/packages/foundry-module/module.json`
### π Quick Start
1. Choose your installation method above
2. Install MCP Bridge module in Foundry VTT
3. Restart Claude Desktop
4. Start creating AI-powered campaigns!
### π Features
- 20 MCP tools for comprehensive Foundry VTT integration
- Actor creation with natural language processing
- Quest management with HTML generation and updates
- Campaign system with multi-part structure and progress tracking
- Dice roll coordination between Claude and Foundry players
- Actor ownership management with bulk operations
- Enhanced creature index for instant monster searches
For full documentation, visit: https://github.com/adambdooley/foundry-vtt-mcp
files: |
installer/build/FoundryMCPServer-Setup-${{ env.PACKAGE_VERSION }}.exe
foundry-vtt-mcp.zip
foundry-mcp-server-${{ env.PACKAGE_VERSION }}.zip
packages/foundry-module/module.json
generate_release_notes: true
draft: false
prerelease: false
- name: Update Foundry VTT Package Registry
if: startsWith(github.ref, 'refs/tags/v')
run: |
Write-Host "π Updating Foundry VTT Package Registry..."
# Extract package information
$packageJson = Get-Content "packages\foundry-module\module.json" -Raw | ConvertFrom-Json
$packageId = $packageJson.id
$version = "${{ env.PACKAGE_VERSION }}".TrimStart('v')
$manifestUrl = "https://github.com/adambdooley/foundry-vtt-mcp/releases/download/${{ github.ref_name }}/module.json"
$releaseNotesUrl = "https://github.com/adambdooley/foundry-vtt-mcp/releases/tag/${{ github.ref_name }}"
Write-Host "π¦ Package ID: $packageId"
Write-Host "π Version: $version"
Write-Host "π Manifest URL: $manifestUrl"
Write-Host "π Release Notes: $releaseNotesUrl"
# Check if this is a dry run
$isDryRun = '${{ github.event.inputs.dry_run_foundry }}' -eq 'true'
if ($isDryRun) {
Write-Host "π§ͺ DRY RUN MODE: Testing API call without updating registry"
}
# Prepare API request body
$requestBody = @{
id = $packageId
release = @{
version = $version
manifest = $manifestUrl
notes = $releaseNotesUrl
compatibility = @{
minimum = $packageJson.compatibility.minimum
verified = $packageJson.compatibility.verified
maximum = $packageJson.compatibility.maximum
}
}
}
# Add dry-run parameter if testing
if ($isDryRun) {
$requestBody['dry-run'] = $true
}
$requestBodyJson = $requestBody | ConvertTo-Json -Depth 3
Write-Host "π§ Request Body:"
Write-Host $requestBodyJson
try {
# Make API call to Foundry VTT Package Registry
$headers = @{
'Content-Type' = 'application/json'
'Authorization' = '${{ secrets.FOUNDRY_PACKAGE_RELEASE_TOKEN }}'
}
$response = Invoke-RestMethod -Uri "https://foundryvtt.com/_api/packages/release_version/" -Method POST -Headers $headers -Body $requestBodyJson
Write-Host "β
Successfully updated Foundry VTT Package Registry"
Write-Host "π Response: $($response | ConvertTo-Json -Depth 2)"
} catch {
Write-Host "β Failed to update Foundry VTT Package Registry"
# Mask any potential token exposure in error messages
$errorMessage = $_.Exception.Message -replace 'fvttp_[A-Za-z0-9]+', '***MASKED***'
Write-Host "π Error: $errorMessage"
if ($_.Exception.Response) {
$errorResponse = $_.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$errorBody = $reader.ReadToEnd()
# Mask any potential token in error response
$safeErrorBody = $errorBody -replace 'fvttp_[A-Za-z0-9]+', '***MASKED***'
Write-Host "π Error Response: $safeErrorBody"
}
# Fail the workflow if Foundry registry update fails
Write-Host "π¨ Foundry Package Registry update is required for complete release"
exit 1
}