73 lines
2.8 KiB
PowerShell
73 lines
2.8 KiB
PowerShell
# Builds a Cursor MCP install deeplink.
|
|
# config = base64(JSON.stringify({command, args, ...})) -- transport only
|
|
# name = server key (separate query param)
|
|
# Wrapping as { serverName: transport } makes Cursor show command: "".
|
|
# Usage: . scripts\cursor-deeplink.ps1; New-CursorDeeplink -ServerName wordcraft -Config @{ command='...'; args=@('mcp') }
|
|
param(
|
|
[string]$Name,
|
|
[string]$ConfigJson
|
|
)
|
|
. (Join-Path $PSScriptRoot 'lib.ps1')
|
|
|
|
function New-CursorDeeplink([string]$ServerName, $Config) {
|
|
$command = $null
|
|
$argList = @()
|
|
$extra = @{}
|
|
|
|
if ($Config -is [string]) {
|
|
$Config = $Config | ConvertFrom-Json
|
|
}
|
|
|
|
if ($Config -is [hashtable]) {
|
|
if ($Config.ContainsKey($ServerName) -and $Config[$ServerName] -is [hashtable]) {
|
|
$Config = $Config[$ServerName]
|
|
}
|
|
$command = [string]$Config['command']
|
|
if ($Config.ContainsKey('args')) { $argList = @($Config['args']) }
|
|
foreach ($k in $Config.Keys) {
|
|
if ($k -ne 'command' -and $k -ne 'args') { $extra[$k] = $Config[$k] }
|
|
}
|
|
}
|
|
else {
|
|
# PSCustomObject
|
|
$props = @($Config.PSObject.Properties.Name)
|
|
if ($props.Count -eq 1 -and $props[0] -eq $ServerName) {
|
|
$Config = $Config.$ServerName
|
|
$props = @($Config.PSObject.Properties.Name)
|
|
}
|
|
$command = [string]$Config.command
|
|
if ($null -ne $Config.args) { $argList = @($Config.args) }
|
|
foreach ($p in $Config.PSObject.Properties) {
|
|
if ($p.Name -ne 'command' -and $p.Name -ne 'args') { $extra[$p.Name] = $p.Value }
|
|
}
|
|
}
|
|
|
|
# Forward slashes: valid on Windows, safer through JSON/URL pipelines.
|
|
if ($command -match '^[A-Za-z]:\\') {
|
|
$command = $command -replace '\\', '/'
|
|
}
|
|
|
|
$transport = [ordered]@{ command = $command; args = @($argList) }
|
|
foreach ($k in $extra.Keys) { $transport[$k] = $extra[$k] }
|
|
|
|
$jsonPath = [IO.Path]::GetTempFileName()
|
|
try {
|
|
# Write via .NET so Python can re-dump with JSON.stringify-compatible compact form.
|
|
$psJson = ($transport | ConvertTo-Json -Compress -Depth 20)
|
|
[IO.File]::WriteAllText($jsonPath, $psJson, $Utf8NoBom)
|
|
$json = & python -c "import json,sys; print(json.dumps(json.load(open(sys.argv[1],encoding='utf-8')),separators=(',',':'),ensure_ascii=False))" $jsonPath
|
|
if (-not $json) { $json = $psJson }
|
|
}
|
|
finally {
|
|
Remove-Item -LiteralPath $jsonPath -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
$bytes = $Utf8NoBom.GetBytes($json)
|
|
$b64 = [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_')
|
|
return "cursor://anysphere.cursor-deeplink/mcp/install?name=$([Uri]::EscapeDataString($ServerName))&config=$b64"
|
|
}
|
|
|
|
if ($Name -and $ConfigJson) {
|
|
Write-Output (New-CursorDeeplink -ServerName $Name -Config $ConfigJson)
|
|
}
|