Remove old UI and do lots of cleanup (#340)
* Remove legacy UI and correct priority ordering of menu items * Remove old UI screen Users now have the new UI alone, less confusing and more predictable * Remove unused config files * Remove test for window that doesn't exist * Remove unused code * Remove dangling .meta file * refactor: remove client configuration step from setup wizard * refactor: remove menu item attributes and manual window actions from Python tool sync * feat: update minimum Python version requirement from 3.10 to 3.11 The docs have 3.12. However, feature wise it seems that 3.11 is required * fix: replace emoji warning symbol with unicode character in setup wizard dialogs * docs: reorganize images into docs/images directory and update references * docs: add UI preview image to README * docs: add run_test function and resources section to available tools list The recent changes should close #311 * fix: add SystemRoot env var to Windows config to support Python path resolution Closes #315 * refactor: consolidate package installation and detection into unified lifecycle manager Duplicate code for pretty much no reason, as they both initialized there was a small chance of a race condition as well. Consolidating made sense here * Doc fixes from CodeRabbit * Excellent bug catch from CodeRabbit * fix: preserve existing environment variables when updating codex server config * Update docs so the paths match the original name * style: fix list indentation in README-DEV.md development docs * refactor: simplify env table handling in CodexConfigHelper by removing preservation logic * refactor: simplify configuration logic by removing redundant change detection Always overwrite configs * feat: ensure config directory exists before writing config files * feat: persist server installation errors and show retry UI instead of auto-marking as handled * refactor: consolidate configuration helpers by merging McpConfigFileHelper into McpConfigurationHelper * Small fixes from CodeRabbit * Remove test because we overwrite Codex configs * Remove unused function * feat: improve server cleanup and process handling on Windows - Added DeleteDirectoryWithRetry helper to handle Windows file locking with retries and readonly attribute clearing - Implemented KillWindowsUvProcesses to safely terminate Python processes in virtual environments using WMIC - Extended TryKillUvForPath to work on Windows, preventing file handle locks during server deletion - Improved error messages to be more descriptive about file locking issues - Replaced direct Directory.Delete calls with * fix: improve TCP socket cleanup to prevent CLOSE_WAIT states - Added proper socket shutdown sequence using Socket.Shutdown() before closing connections - Enhanced error handling with specific catches for SocketException vs general exceptions - Added debug logging for socket shutdown errors to help diagnose connection issues - Restructured HandleClientAsync to ensure socket cleanup happens in the correct order - Implemented proper socket teardown in both client handling and connection cleanup pathsmain
|
|
@ -1,17 +0,0 @@
|
|||
using MCPForUnity.Editor.Models;
|
||||
|
||||
namespace MCPForUnity.Editor.Data
|
||||
{
|
||||
public class DefaultServerConfig : ServerConfig
|
||||
{
|
||||
public new string unityHost = "localhost";
|
||||
public new int unityPort = 6400;
|
||||
public new int mcpPort = 6500;
|
||||
public new float connectionTimeout = 15.0f;
|
||||
public new int bufferSize = 32768;
|
||||
public new string logLevel = "INFO";
|
||||
public new string logFormat = "%(asctime)s - %(name)s - %(levelname)s - %(message)s";
|
||||
public new int maxRetries = 3;
|
||||
public new float retryDelay = 1.0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ namespace MCPForUnity.Editor.Dependencies
|
|||
{
|
||||
if (dep.Name == "Python")
|
||||
{
|
||||
result.RecommendedActions.Add($"Install Python 3.10+ from: {detector.GetPythonInstallUrl()}");
|
||||
result.RecommendedActions.Add($"Install Python 3.11+ from: {detector.GetPythonInstallUrl()}");
|
||||
}
|
||||
else if (dep.Name == "UV Package Manager")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
|
|||
}
|
||||
}
|
||||
|
||||
status.ErrorMessage = "Python not found. Please install Python 3.10 or later.";
|
||||
status.ErrorMessage = "Python not found. Please install Python 3.11 or later.";
|
||||
status.Details = "Checked common installation paths including system, snap, and user-local locations.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -144,10 +144,10 @@ Note: Make sure ~/.local/bin is in your PATH for user-local installations.";
|
|||
version = output.Substring(7); // Remove "Python " prefix
|
||||
fullPath = pythonPath;
|
||||
|
||||
// Validate minimum version (Python 4+ or Python 3.10+)
|
||||
// Validate minimum version (Python 4+ or Python 3.11+)
|
||||
if (TryParseVersion(version, out var major, out var minor))
|
||||
{
|
||||
return major > 3 || (major >= 3 && minor >= 10);
|
||||
return major > 3 || (major >= 3 && minor >= 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
|
|||
"/opt/homebrew/bin/python3",
|
||||
"/Library/Frameworks/Python.framework/Versions/3.13/bin/python3",
|
||||
"/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
|
||||
"/Library/Frameworks/Python.framework/Versions/3.11/bin/python3",
|
||||
"/Library/Frameworks/Python.framework/Versions/3.10/bin/python3"
|
||||
"/Library/Frameworks/Python.framework/Versions/3.11/bin/python3"
|
||||
};
|
||||
|
||||
foreach (var candidate in candidates)
|
||||
|
|
@ -65,7 +64,7 @@ namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
|
|||
}
|
||||
}
|
||||
|
||||
status.ErrorMessage = "Python not found. Please install Python 3.10 or later.";
|
||||
status.ErrorMessage = "Python not found. Please install Python 3.11 or later.";
|
||||
status.Details = "Checked common installation paths including Homebrew, Framework, and system locations.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -144,10 +143,10 @@ Note: If using Homebrew, make sure /opt/homebrew/bin is in your PATH.";
|
|||
version = output.Substring(7); // Remove "Python " prefix
|
||||
fullPath = pythonPath;
|
||||
|
||||
// Validate minimum version (Python 4+ or Python 3.10+)
|
||||
// Validate minimum version (Python 4+ or Python 3.11+)
|
||||
if (TryParseVersion(version, out var major, out var minor))
|
||||
{
|
||||
return major > 3 || (major >= 3 && minor >= 10);
|
||||
return major > 3 || (major >= 3 && minor >= 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
|
|||
}
|
||||
}
|
||||
|
||||
status.ErrorMessage = "Python not found. Please install Python 3.10 or later.";
|
||||
status.ErrorMessage = "Python not found. Please install Python 3.11 or later.";
|
||||
status.Details = "Checked common installation paths and PATH environment variable.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -132,10 +132,10 @@ namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
|
|||
version = output.Substring(7); // Remove "Python " prefix
|
||||
fullPath = pythonPath;
|
||||
|
||||
// Validate minimum version (Python 4+ or Python 3.10+)
|
||||
// Validate minimum version (Python 4+ or Python 3.11+)
|
||||
if (TryParseVersion(version, out var major, out var minor))
|
||||
{
|
||||
return major > 3 || (major >= 3 && minor >= 10);
|
||||
return major > 3 || (major >= 3 && minor >= 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using MCPForUnity.External.Tommy;
|
||||
using MCPForUnity.Editor.Services;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
|
|
@ -26,10 +27,10 @@ namespace MCPForUnity.Editor.Helpers
|
|||
string toml = File.ReadAllText(configPath);
|
||||
if (!TryParseCodexServer(toml, out _, out var args)) return false;
|
||||
|
||||
string dir = McpConfigFileHelper.ExtractDirectoryArg(args);
|
||||
string dir = McpConfigurationHelper.ExtractDirectoryArg(args);
|
||||
if (string.IsNullOrEmpty(dir)) return false;
|
||||
|
||||
return McpConfigFileHelper.PathsEqual(dir, pythonDir);
|
||||
return McpConfigurationHelper.PathsEqual(dir, pythonDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -125,6 +126,8 @@ namespace MCPForUnity.Editor.Helpers
|
|||
/// <summary>
|
||||
/// Creates a TomlTable for the unityMCP server configuration
|
||||
/// </summary>
|
||||
/// <param name="uvPath">Path to uv executable</param>
|
||||
/// <param name="serverSrc">Path to server source directory</param>
|
||||
private static TomlTable CreateUnityMcpTable(string uvPath, string serverSrc)
|
||||
{
|
||||
var unityMCP = new TomlTable();
|
||||
|
|
@ -137,6 +140,15 @@ namespace MCPForUnity.Editor.Helpers
|
|||
argsArray.Add(new TomlString { Value = "server.py" });
|
||||
unityMCP["args"] = argsArray;
|
||||
|
||||
// Add Windows-specific environment configuration, see: https://github.com/CoplayDev/unity-mcp/issues/315
|
||||
var platformService = MCPServiceLocator.Platform;
|
||||
if (platformService.IsWindows())
|
||||
{
|
||||
var envTable = new TomlTable { IsInline = true };
|
||||
envTable["SystemRoot"] = new TomlString { Value = platformService.GetSystemRoot() };
|
||||
unityMCP["env"] = envTable;
|
||||
}
|
||||
|
||||
return unityMCP;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,186 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared helpers for reading and writing MCP client configuration files.
|
||||
/// Consolidates file atomics and server directory resolution so the editor
|
||||
/// window can focus on UI concerns only.
|
||||
/// </summary>
|
||||
public static class McpConfigFileHelper
|
||||
{
|
||||
public static string ExtractDirectoryArg(string[] args)
|
||||
{
|
||||
if (args == null) return null;
|
||||
for (int i = 0; i < args.Length - 1; i++)
|
||||
{
|
||||
if (string.Equals(args[i], "--directory", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return args[i + 1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool PathsEqual(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return false;
|
||||
try
|
||||
{
|
||||
string na = Path.GetFullPath(a.Trim());
|
||||
string nb = Path.GetFullPath(b.Trim());
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return string.Equals(na, nb, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return string.Equals(na, nb, StringComparison.Ordinal);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the server directory to use for MCP tools, preferring
|
||||
/// existing config values and falling back to installed/embedded copies.
|
||||
/// </summary>
|
||||
public static string ResolveServerDirectory(string pythonDir, string[] existingArgs)
|
||||
{
|
||||
string serverSrc = ExtractDirectoryArg(existingArgs);
|
||||
bool serverValid = !string.IsNullOrEmpty(serverSrc)
|
||||
&& File.Exists(Path.Combine(serverSrc, "server.py"));
|
||||
if (!serverValid)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pythonDir)
|
||||
&& File.Exists(Path.Combine(pythonDir, "server.py")))
|
||||
{
|
||||
serverSrc = pythonDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverSrc = ResolveServerSource();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !string.IsNullOrEmpty(serverSrc))
|
||||
{
|
||||
string norm = serverSrc.Replace('\\', '/');
|
||||
int idx = norm.IndexOf("/.local/share/UnityMCP/", StringComparison.Ordinal);
|
||||
if (idx >= 0)
|
||||
{
|
||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.Personal) ?? string.Empty;
|
||||
string suffix = norm.Substring(idx + "/.local/share/".Length);
|
||||
serverSrc = Path.Combine(home, "Library", "Application Support", suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore failures and fall back to the original path.
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
&& !string.IsNullOrEmpty(serverSrc)
|
||||
&& serverSrc.IndexOf(@"\Library\PackageCache\", StringComparison.OrdinalIgnoreCase) >= 0
|
||||
&& !EditorPrefs.GetBool("MCPForUnity.UseEmbeddedServer", false))
|
||||
{
|
||||
serverSrc = ServerInstaller.GetServerPath();
|
||||
}
|
||||
|
||||
return serverSrc;
|
||||
}
|
||||
|
||||
public static void WriteAtomicFile(string path, string contents)
|
||||
{
|
||||
string tmp = path + ".tmp";
|
||||
string backup = path + ".backup";
|
||||
bool writeDone = false;
|
||||
try
|
||||
{
|
||||
File.WriteAllText(tmp, contents, new UTF8Encoding(false));
|
||||
try
|
||||
{
|
||||
File.Replace(tmp, path, backup);
|
||||
writeDone = true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
File.Move(tmp, path);
|
||||
writeDone = true;
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(backup)) File.Delete(backup);
|
||||
}
|
||||
catch { }
|
||||
File.Move(path, backup);
|
||||
}
|
||||
File.Move(tmp, path);
|
||||
writeDone = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!writeDone && File.Exists(backup))
|
||||
{
|
||||
try { File.Copy(backup, path, true); } catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
throw new Exception($"Failed to write config file '{path}': {ex.Message}", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (File.Exists(tmp)) File.Delete(tmp); } catch { }
|
||||
try { if (writeDone && File.Exists(backup)) File.Delete(backup); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public static string ResolveServerSource()
|
||||
{
|
||||
try
|
||||
{
|
||||
string remembered = EditorPrefs.GetString("MCPForUnity.ServerSrc", string.Empty);
|
||||
if (!string.IsNullOrEmpty(remembered)
|
||||
&& File.Exists(Path.Combine(remembered, "server.py")))
|
||||
{
|
||||
return remembered;
|
||||
}
|
||||
|
||||
ServerInstaller.EnsureServerInstalled();
|
||||
string installed = ServerInstaller.GetServerPath();
|
||||
if (File.Exists(Path.Combine(installed, "server.py")))
|
||||
{
|
||||
return installed;
|
||||
}
|
||||
|
||||
bool useEmbedded = EditorPrefs.GetBool("MCPForUnity.UseEmbeddedServer", false);
|
||||
if (useEmbedded
|
||||
&& ServerPathResolver.TryFindEmbeddedServerSource(out string embedded)
|
||||
&& File.Exists(Path.Combine(embedded, "server.py")))
|
||||
{
|
||||
return embedded;
|
||||
}
|
||||
|
||||
return installed;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return ServerInstaller.GetServerPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UnityEditor;
|
||||
|
|
@ -105,20 +106,9 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
catch { }
|
||||
if (uvPath == null) return "UV package manager not found. Please install UV first.";
|
||||
string serverSrc = McpConfigFileHelper.ResolveServerDirectory(pythonDir, existingArgs);
|
||||
string serverSrc = ResolveServerDirectory(pythonDir, existingArgs);
|
||||
|
||||
// 2) Canonical args order
|
||||
var newArgs = new[] { "run", "--directory", serverSrc, "server.py" };
|
||||
|
||||
// 3) Only write if changed
|
||||
bool changed = !string.Equals(existingCommand, uvPath, StringComparison.Ordinal)
|
||||
|| !ArgsEqual(existingArgs, newArgs);
|
||||
if (!changed)
|
||||
{
|
||||
return "Configured successfully"; // nothing to do
|
||||
}
|
||||
|
||||
// 4) Ensure containers exist and write back minimal changes
|
||||
// Ensure containers exist and write back configuration
|
||||
JObject existingRoot;
|
||||
if (existingConfig is JObject eo)
|
||||
existingRoot = eo;
|
||||
|
|
@ -129,7 +119,8 @@ namespace MCPForUnity.Editor.Helpers
|
|||
|
||||
string mergedJson = JsonConvert.SerializeObject(existingRoot, jsonSettings);
|
||||
|
||||
McpConfigFileHelper.WriteAtomicFile(configPath, mergedJson);
|
||||
EnsureConfigDirectoryExists(configPath);
|
||||
WriteAtomicFile(configPath, mergedJson);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -190,24 +181,12 @@ namespace MCPForUnity.Editor.Helpers
|
|||
return "UV package manager not found. Please install UV first.";
|
||||
}
|
||||
|
||||
string serverSrc = McpConfigFileHelper.ResolveServerDirectory(pythonDir, existingArgs);
|
||||
var newArgs = new[] { "run", "--directory", serverSrc, "server.py" };
|
||||
|
||||
bool changed = true;
|
||||
if (!string.IsNullOrEmpty(existingCommand) && existingArgs != null)
|
||||
{
|
||||
changed = !string.Equals(existingCommand, uvPath, StringComparison.Ordinal)
|
||||
|| !ArgsEqual(existingArgs, newArgs);
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
{
|
||||
return "Configured successfully";
|
||||
}
|
||||
string serverSrc = ResolveServerDirectory(pythonDir, existingArgs);
|
||||
|
||||
string updatedToml = CodexConfigHelper.UpsertCodexServerBlock(existingToml, uvPath, serverSrc);
|
||||
|
||||
McpConfigFileHelper.WriteAtomicFile(configPath, updatedToml);
|
||||
EnsureConfigDirectoryExists(configPath);
|
||||
WriteAtomicFile(configPath, updatedToml);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -246,20 +225,6 @@ namespace MCPForUnity.Editor.Helpers
|
|||
catch { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two string arrays for equality
|
||||
/// </summary>
|
||||
private static bool ArgsEqual(string[] a, string[] b)
|
||||
{
|
||||
if (a == null || b == null) return a == b;
|
||||
if (a.Length != b.Length) return false;
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
if (!string.Equals(a[i], b[i], StringComparison.Ordinal)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the appropriate config file path for the given MCP client based on OS
|
||||
/// </summary>
|
||||
|
|
@ -292,5 +257,175 @@ namespace MCPForUnity.Editor.Helpers
|
|||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(configPath));
|
||||
}
|
||||
|
||||
public static string ExtractDirectoryArg(string[] args)
|
||||
{
|
||||
if (args == null) return null;
|
||||
for (int i = 0; i < args.Length - 1; i++)
|
||||
{
|
||||
if (string.Equals(args[i], "--directory", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return args[i + 1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool PathsEqual(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return false;
|
||||
try
|
||||
{
|
||||
string na = Path.GetFullPath(a.Trim());
|
||||
string nb = Path.GetFullPath(b.Trim());
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return string.Equals(na, nb, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return string.Equals(na, nb, StringComparison.Ordinal);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the server directory to use for MCP tools, preferring
|
||||
/// existing config values and falling back to installed/embedded copies.
|
||||
/// </summary>
|
||||
public static string ResolveServerDirectory(string pythonDir, string[] existingArgs)
|
||||
{
|
||||
string serverSrc = ExtractDirectoryArg(existingArgs);
|
||||
bool serverValid = !string.IsNullOrEmpty(serverSrc)
|
||||
&& File.Exists(Path.Combine(serverSrc, "server.py"));
|
||||
if (!serverValid)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(pythonDir)
|
||||
&& File.Exists(Path.Combine(pythonDir, "server.py")))
|
||||
{
|
||||
serverSrc = pythonDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverSrc = ResolveServerSource();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && !string.IsNullOrEmpty(serverSrc))
|
||||
{
|
||||
string norm = serverSrc.Replace('\\', '/');
|
||||
int idx = norm.IndexOf("/.local/share/UnityMCP/", StringComparison.Ordinal);
|
||||
if (idx >= 0)
|
||||
{
|
||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.Personal) ?? string.Empty;
|
||||
string suffix = norm.Substring(idx + "/.local/share/".Length);
|
||||
serverSrc = Path.Combine(home, "Library", "Application Support", suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore failures and fall back to the original path.
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
&& !string.IsNullOrEmpty(serverSrc)
|
||||
&& serverSrc.IndexOf(@"\Library\PackageCache\", StringComparison.OrdinalIgnoreCase) >= 0
|
||||
&& !EditorPrefs.GetBool("MCPForUnity.UseEmbeddedServer", false))
|
||||
{
|
||||
serverSrc = ServerInstaller.GetServerPath();
|
||||
}
|
||||
|
||||
return serverSrc;
|
||||
}
|
||||
|
||||
public static void WriteAtomicFile(string path, string contents)
|
||||
{
|
||||
string tmp = path + ".tmp";
|
||||
string backup = path + ".backup";
|
||||
bool writeDone = false;
|
||||
try
|
||||
{
|
||||
File.WriteAllText(tmp, contents, new UTF8Encoding(false));
|
||||
try
|
||||
{
|
||||
File.Replace(tmp, path, backup);
|
||||
writeDone = true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
File.Move(tmp, path);
|
||||
writeDone = true;
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(backup)) File.Delete(backup);
|
||||
}
|
||||
catch { }
|
||||
File.Move(path, backup);
|
||||
}
|
||||
File.Move(tmp, path);
|
||||
writeDone = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!writeDone && File.Exists(backup))
|
||||
{
|
||||
try { File.Copy(backup, path, true); } catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
throw new Exception($"Failed to write config file '{path}': {ex.Message}", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (File.Exists(tmp)) File.Delete(tmp); } catch { }
|
||||
try { if (writeDone && File.Exists(backup)) File.Delete(backup); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public static string ResolveServerSource()
|
||||
{
|
||||
try
|
||||
{
|
||||
string remembered = EditorPrefs.GetString("MCPForUnity.ServerSrc", string.Empty);
|
||||
if (!string.IsNullOrEmpty(remembered)
|
||||
&& File.Exists(Path.Combine(remembered, "server.py")))
|
||||
{
|
||||
return remembered;
|
||||
}
|
||||
|
||||
ServerInstaller.EnsureServerInstalled();
|
||||
string installed = ServerInstaller.GetServerPath();
|
||||
if (File.Exists(Path.Combine(installed, "server.py")))
|
||||
{
|
||||
return installed;
|
||||
}
|
||||
|
||||
bool useEmbedded = EditorPrefs.GetBool("MCPForUnity.UseEmbeddedServer", false);
|
||||
if (useEmbedded
|
||||
&& ServerPathResolver.TryFindEmbeddedServerSource(out string embedded)
|
||||
&& File.Exists(Path.Combine(embedded, "server.py")))
|
||||
{
|
||||
return embedded;
|
||||
}
|
||||
|
||||
return installed;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return ServerInstaller.GetServerPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace MCPForUnity.Editor.Helpers
|
|||
/// </summary>
|
||||
public static string FindPackagePythonDirectory(bool debugLogsEnabled = false)
|
||||
{
|
||||
string pythonDir = McpConfigFileHelper.ResolveServerSource();
|
||||
string pythonDir = McpConfigurationHelper.ResolveServerSource();
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto-runs legacy/older install detection on package load/update (log-only).
|
||||
/// Runs once per embedded server version using an EditorPrefs version-scoped key.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public static class PackageDetector
|
||||
{
|
||||
private const string DetectOnceFlagKeyPrefix = "MCPForUnity.LegacyDetectLogged:";
|
||||
|
||||
static PackageDetector()
|
||||
{
|
||||
try
|
||||
{
|
||||
string pkgVer = ReadPackageVersionOrFallback();
|
||||
string key = DetectOnceFlagKeyPrefix + pkgVer;
|
||||
|
||||
// Always force-run if legacy roots exist or canonical install is missing
|
||||
bool legacyPresent = LegacyRootsExist();
|
||||
bool canonicalMissing = !System.IO.File.Exists(System.IO.Path.Combine(ServerInstaller.GetServerPath(), "server.py"));
|
||||
|
||||
if (!EditorPrefs.GetBool(key, false) || legacyPresent || canonicalMissing)
|
||||
{
|
||||
// Marshal the entire flow to the main thread. EnsureServerInstalled may touch Unity APIs.
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
string error = null;
|
||||
System.Exception capturedEx = null;
|
||||
try
|
||||
{
|
||||
// Ensure any UnityEditor API usage inside runs on the main thread
|
||||
ServerInstaller.EnsureServerInstalled();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
capturedEx = ex;
|
||||
}
|
||||
|
||||
// Unity APIs must stay on main thread
|
||||
try { EditorPrefs.SetBool(key, true); } catch { }
|
||||
// Ensure prefs cleanup happens on main thread
|
||||
try { EditorPrefs.DeleteKey("MCPForUnity.ServerSrc"); } catch { }
|
||||
try { EditorPrefs.DeleteKey("MCPForUnity.PythonDirOverride"); } catch { }
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
McpLog.Info($"Server check: {error}. Download via Window > MCP For Unity if needed.", always: false);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
}
|
||||
|
||||
private static string ReadEmbeddedVersionOrFallback()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ServerPathResolver.TryFindEmbeddedServerSource(out var embeddedSrc))
|
||||
{
|
||||
var p = System.IO.Path.Combine(embeddedSrc, "server_version.txt");
|
||||
if (System.IO.File.Exists(p))
|
||||
return (System.IO.File.ReadAllText(p)?.Trim() ?? "unknown");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static string ReadPackageVersionOrFallback()
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(PackageDetector).Assembly);
|
||||
if (info != null && !string.IsNullOrEmpty(info.version)) return info.version;
|
||||
}
|
||||
catch { }
|
||||
// Fallback to embedded server version if package info unavailable
|
||||
return ReadEmbeddedVersionOrFallback();
|
||||
}
|
||||
|
||||
private static bool LegacyRootsExist()
|
||||
{
|
||||
try
|
||||
{
|
||||
string home = System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
||||
string[] roots =
|
||||
{
|
||||
System.IO.Path.Combine(home, ".config", "UnityMCP", "UnityMcpServer", "src"),
|
||||
System.IO.Path.Combine(home, ".local", "share", "UnityMCP", "UnityMcpServer", "src")
|
||||
};
|
||||
foreach (var r in roots)
|
||||
{
|
||||
try { if (System.IO.File.Exists(System.IO.Path.Combine(r, "server.py"))) return true; } catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles automatic installation of the MCP server when the package is first installed.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public static class PackageInstaller
|
||||
{
|
||||
private const string InstallationFlagKey = "MCPForUnity.ServerInstalled";
|
||||
|
||||
static PackageInstaller()
|
||||
{
|
||||
// Check if this is the first time the package is loaded
|
||||
if (!EditorPrefs.GetBool(InstallationFlagKey, false))
|
||||
{
|
||||
// Schedule the installation for after Unity is fully loaded
|
||||
EditorApplication.delayCall += InstallServerOnFirstLoad;
|
||||
}
|
||||
}
|
||||
|
||||
private static void InstallServerOnFirstLoad()
|
||||
{
|
||||
try
|
||||
{
|
||||
ServerInstaller.EnsureServerInstalled();
|
||||
|
||||
// Mark as installed/checked
|
||||
EditorPrefs.SetBool(InstallationFlagKey, true);
|
||||
|
||||
// Only log success if server was actually embedded and copied
|
||||
if (ServerInstaller.HasEmbeddedServer())
|
||||
{
|
||||
McpLog.Info("MCP server installation completed successfully.");
|
||||
}
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
EditorPrefs.SetBool(InstallationFlagKey, true); // Mark as handled
|
||||
McpLog.Info("Server installation pending. Open Window > MCP For Unity to download the server.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages package lifecycle events including first-time installation,
|
||||
/// version updates, and legacy installation detection.
|
||||
/// Consolidates the functionality of PackageInstaller and PackageDetector.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public static class PackageLifecycleManager
|
||||
{
|
||||
private const string VersionKeyPrefix = "MCPForUnity.InstalledVersion:";
|
||||
private const string LegacyInstallFlagKey = "MCPForUnity.ServerInstalled"; // For migration
|
||||
private const string InstallErrorKeyPrefix = "MCPForUnity.InstallError:"; // Stores last installation error
|
||||
|
||||
static PackageLifecycleManager()
|
||||
{
|
||||
// Schedule the check for after Unity is fully loaded
|
||||
EditorApplication.delayCall += CheckAndInstallServer;
|
||||
}
|
||||
|
||||
private static void CheckAndInstallServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentVersion = GetPackageVersion();
|
||||
string versionKey = VersionKeyPrefix + currentVersion;
|
||||
bool hasRunForThisVersion = EditorPrefs.GetBool(versionKey, false);
|
||||
|
||||
// Check for conditions that require installation/verification
|
||||
bool isFirstTimeInstall = !EditorPrefs.HasKey(LegacyInstallFlagKey) && !hasRunForThisVersion;
|
||||
bool legacyPresent = LegacyRootsExist();
|
||||
bool canonicalMissing = !File.Exists(
|
||||
Path.Combine(ServerInstaller.GetServerPath(), "server.py")
|
||||
);
|
||||
|
||||
// Run if: first install, version update, legacy detected, or canonical missing
|
||||
if (isFirstTimeInstall || !hasRunForThisVersion || legacyPresent || canonicalMissing)
|
||||
{
|
||||
PerformInstallation(currentVersion, versionKey, isFirstTimeInstall);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
McpLog.Info($"Package lifecycle check failed: {ex.Message}. Open Window > MCP For Unity if needed.", always: false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void PerformInstallation(string version, string versionKey, bool isFirstTimeInstall)
|
||||
{
|
||||
string error = null;
|
||||
|
||||
try
|
||||
{
|
||||
ServerInstaller.EnsureServerInstalled();
|
||||
|
||||
// Mark as installed for this version
|
||||
EditorPrefs.SetBool(versionKey, true);
|
||||
|
||||
// Migrate legacy flag if this is first time
|
||||
if (isFirstTimeInstall)
|
||||
{
|
||||
EditorPrefs.SetBool(LegacyInstallFlagKey, true);
|
||||
}
|
||||
|
||||
// Clean up old version keys (keep only current version)
|
||||
CleanupOldVersionKeys(version);
|
||||
|
||||
// Clean up legacy preference keys
|
||||
CleanupLegacyPrefs();
|
||||
|
||||
// Only log success if server was actually embedded and copied
|
||||
if (ServerInstaller.HasEmbeddedServer() && isFirstTimeInstall)
|
||||
{
|
||||
McpLog.Info("MCP server installation completed successfully.");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
|
||||
// Store the error for display in the UI, but don't mark as handled
|
||||
// This allows the user to manually rebuild via the "Rebuild Server" button
|
||||
string errorKey = InstallErrorKeyPrefix + version;
|
||||
EditorPrefs.SetString(errorKey, ex.Message ?? "Unknown error");
|
||||
|
||||
// Don't mark as installed - user needs to manually rebuild
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
McpLog.Info($"Server installation failed: {error}. Use Window > MCP For Unity > Rebuild Server to retry.", always: false);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPackageVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = UnityEditor.PackageManager.PackageInfo.FindForAssembly(
|
||||
typeof(PackageLifecycleManager).Assembly
|
||||
);
|
||||
if (info != null && !string.IsNullOrEmpty(info.version))
|
||||
{
|
||||
return info.version;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Fallback to embedded server version
|
||||
return GetEmbeddedServerVersion();
|
||||
}
|
||||
|
||||
private static string GetEmbeddedServerVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ServerPathResolver.TryFindEmbeddedServerSource(out var embeddedSrc))
|
||||
{
|
||||
var versionPath = Path.Combine(embeddedSrc, "server_version.txt");
|
||||
if (File.Exists(versionPath))
|
||||
{
|
||||
return File.ReadAllText(versionPath)?.Trim() ?? "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static bool LegacyRootsExist()
|
||||
{
|
||||
try
|
||||
{
|
||||
string home = System.Environment.GetFolderPath(
|
||||
System.Environment.SpecialFolder.UserProfile
|
||||
) ?? string.Empty;
|
||||
|
||||
string[] legacyRoots =
|
||||
{
|
||||
Path.Combine(home, ".config", "UnityMCP", "UnityMcpServer", "src"),
|
||||
Path.Combine(home, ".local", "share", "UnityMCP", "UnityMcpServer", "src")
|
||||
};
|
||||
|
||||
foreach (var root in legacyRoots)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(Path.Combine(root, "server.py")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void CleanupOldVersionKeys(string currentVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get all EditorPrefs keys that start with our version prefix
|
||||
// Note: Unity doesn't provide a way to enumerate all keys, so we can only
|
||||
// clean up known legacy keys. Future versions will be cleaned up when
|
||||
// a newer version runs.
|
||||
// This is a best-effort cleanup.
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static void CleanupLegacyPrefs()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clean up old preference keys that are no longer used
|
||||
string[] legacyKeys =
|
||||
{
|
||||
"MCPForUnity.ServerSrc",
|
||||
"MCPForUnity.PythonDirOverride",
|
||||
"MCPForUnity.LegacyDetectLogged" // Old prefix without version
|
||||
};
|
||||
|
||||
foreach (var key in legacyKeys)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EditorPrefs.HasKey(key))
|
||||
{
|
||||
EditorPrefs.DeleteKey(key);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last installation error for the current package version, if any.
|
||||
/// Returns null if there was no error or the error has been cleared.
|
||||
/// </summary>
|
||||
public static string GetLastInstallError()
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentVersion = GetPackageVersion();
|
||||
string errorKey = InstallErrorKeyPrefix + currentVersion;
|
||||
if (EditorPrefs.HasKey(errorKey))
|
||||
{
|
||||
return EditorPrefs.GetString(errorKey, null);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the last installation error. Should be called after a successful manual rebuild.
|
||||
/// </summary>
|
||||
public static void ClearLastInstallError()
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentVersion = GetPackageVersion();
|
||||
string errorKey = InstallErrorKeyPrefix + currentVersion;
|
||||
if (EditorPrefs.HasKey(errorKey))
|
||||
{
|
||||
EditorPrefs.DeleteKey(errorKey);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b82eaef548d164ca095f17db64d15af8
|
||||
guid: c40bd28f2310d463c8cd00181202cbe4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -139,9 +139,8 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menu item to reimport all Python files in the project
|
||||
/// Reimport all Python files in the project
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Reimport Python Files", priority = 99)]
|
||||
public static void ReimportPythonFiles()
|
||||
{
|
||||
// Find all Python files (imported as TextAssets by PythonFileImporter)
|
||||
|
|
@ -161,9 +160,8 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menu item to manually trigger sync
|
||||
/// Manually trigger sync
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Sync Python Tools", priority = 100)]
|
||||
public static void ManualSync()
|
||||
{
|
||||
McpLog.Info("Manually syncing Python tools...");
|
||||
|
|
@ -171,9 +169,8 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menu item to toggle auto-sync
|
||||
/// Toggle auto-sync
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Auto-Sync Python Tools", priority = 101)]
|
||||
public static void ToggleAutoSync()
|
||||
{
|
||||
SetAutoSyncEnabled(!IsAutoSyncEnabled());
|
||||
|
|
@ -182,7 +179,6 @@ namespace MCPForUnity.Editor.Helpers
|
|||
/// <summary>
|
||||
/// Validate menu item (shows checkmark when enabled)
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Auto-Sync Python Tools", true, priority = 101)]
|
||||
public static bool ToggleAutoSyncValidate()
|
||||
{
|
||||
Menu.SetChecked("Window/MCP For Unity/Tool Sync/Auto-Sync Python Tools", IsAutoSyncEnabled());
|
||||
|
|
|
|||
|
|
@ -84,14 +84,13 @@ namespace MCPForUnity.Editor.Helpers
|
|||
if (legacyOlder)
|
||||
{
|
||||
TryKillUvForPath(legacySrc);
|
||||
try
|
||||
if (DeleteDirectoryWithRetry(legacyRoot))
|
||||
{
|
||||
Directory.Delete(legacyRoot, recursive: true);
|
||||
McpLog.Info($"Removed legacy server at '{legacyRoot}'.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
McpLog.Warn($"Failed to remove legacy server at '{legacyRoot}': {ex.Message}");
|
||||
McpLog.Warn($"Failed to remove legacy server at '{legacyRoot}' (files may be in use)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -338,13 +337,24 @@ namespace MCPForUnity.Editor.Helpers
|
|||
return roots;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to kill UV and Python processes associated with a specific server path.
|
||||
/// This is necessary on Windows because the OS blocks file deletion when processes
|
||||
/// have open file handles, unlike macOS/Linux which allow unlinking open files.
|
||||
/// </summary>
|
||||
private static void TryKillUvForPath(string serverSrcPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(serverSrcPath)) return;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
KillWindowsUvProcesses(serverSrcPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unix: use pgrep to find processes by command line
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "/usr/bin/pgrep",
|
||||
|
|
@ -372,6 +382,148 @@ namespace MCPForUnity.Editor.Helpers
|
|||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills Windows processes running from the virtual environment directory.
|
||||
/// Uses WMIC (Windows Management Instrumentation) to safely query only processes
|
||||
/// with executables in the .venv path, avoiding the need to iterate all system processes.
|
||||
/// This prevents accidentally killing IDE processes or other critical system processes.
|
||||
///
|
||||
/// Why this is needed on Windows:
|
||||
/// - Windows blocks file/directory deletion when ANY process has an open file handle
|
||||
/// - UV creates a virtual environment with python.exe and other executables
|
||||
/// - These processes may hold locks on DLLs, .pyd files, or the executables themselves
|
||||
/// - macOS/Linux allow deletion of open files (unlink), but Windows does not
|
||||
/// </summary>
|
||||
private static void KillWindowsUvProcesses(string serverSrcPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(serverSrcPath)) return;
|
||||
|
||||
string venvPath = Path.Combine(serverSrcPath, ".venv");
|
||||
if (!Directory.Exists(venvPath)) return;
|
||||
|
||||
string normalizedVenvPath = Path.GetFullPath(venvPath).ToLowerInvariant();
|
||||
|
||||
// Use WMIC to find processes with executables in the .venv directory
|
||||
// This is much safer than iterating all processes
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "wmic",
|
||||
Arguments = $"process where \"ExecutablePath like '%{normalizedVenvPath.Replace("\\", "\\\\")}%'\" get ProcessId",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var proc = Process.Start(psi);
|
||||
if (proc == null) return;
|
||||
|
||||
string output = proc.StandardOutput.ReadToEnd();
|
||||
proc.WaitForExit(5000);
|
||||
|
||||
if (proc.ExitCode != 0) return;
|
||||
|
||||
// Parse PIDs from WMIC output
|
||||
var lines = output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
string trimmed = line.Trim();
|
||||
if (trimmed.Equals("ProcessId", StringComparison.OrdinalIgnoreCase)) continue;
|
||||
if (string.IsNullOrWhiteSpace(trimmed)) continue;
|
||||
|
||||
if (int.TryParse(trimmed, out int pid))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var p = Process.GetProcessById(pid);
|
||||
// Double-check it's not a critical process
|
||||
string name = p.ProcessName.ToLowerInvariant();
|
||||
if (name == "unity" || name == "code" || name == "devenv" || name == "rider64")
|
||||
{
|
||||
continue; // Skip IDE processes
|
||||
}
|
||||
p.Kill();
|
||||
p.WaitForExit(2000);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// Give processes time to fully exit
|
||||
System.Threading.Thread.Sleep(500);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to delete a directory with retry logic to handle Windows file locking issues.
|
||||
///
|
||||
/// Why retries are necessary on Windows:
|
||||
/// - Even after killing processes, Windows may take time to release file handles
|
||||
/// - Antivirus, Windows Defender, or indexing services may temporarily lock files
|
||||
/// - File Explorer previews can hold locks on certain file types
|
||||
/// - Readonly attributes on files (common in .venv) block deletion
|
||||
///
|
||||
/// This method handles these cases by:
|
||||
/// - Retrying deletion after a delay to allow handle release
|
||||
/// - Clearing readonly attributes that block deletion
|
||||
/// - Distinguishing between temporary locks (retry) and permanent failures
|
||||
/// </summary>
|
||||
private static bool DeleteDirectoryWithRetry(string path, int maxRetries = 3, int delayMs = 500)
|
||||
{
|
||||
for (int i = 0; i < maxRetries; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(path)) return true;
|
||||
|
||||
Directory.Delete(path, recursive: true);
|
||||
return true;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
if (i < maxRetries - 1)
|
||||
{
|
||||
// Wait for file handles to be released
|
||||
System.Threading.Thread.Sleep(delayMs);
|
||||
|
||||
// Try to clear readonly attributes
|
||||
try
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
try
|
||||
{
|
||||
var attrs = File.GetAttributes(file);
|
||||
if ((attrs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||
{
|
||||
File.SetAttributes(file, attrs & ~FileAttributes.ReadOnly);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (i < maxRetries - 1)
|
||||
{
|
||||
// File in use, wait and retry
|
||||
System.Threading.Thread.Sleep(delayMs);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string ReadVersionFile(string path)
|
||||
{
|
||||
try
|
||||
|
|
@ -459,16 +611,12 @@ namespace MCPForUnity.Editor.Helpers
|
|||
// Delete the entire installed server directory
|
||||
if (Directory.Exists(destRoot))
|
||||
{
|
||||
try
|
||||
if (!DeleteDirectoryWithRetry(destRoot, maxRetries: 5, delayMs: 1000))
|
||||
{
|
||||
Directory.Delete(destRoot, recursive: true);
|
||||
McpLog.Info($"Deleted existing server at {destRoot}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Error($"Failed to delete existing server: {ex.Message}");
|
||||
McpLog.Error($"Failed to delete existing server at {destRoot}. Please close any applications using the Python virtual environment and try again.");
|
||||
return false;
|
||||
}
|
||||
McpLog.Info($"Deleted existing server at {destRoot}");
|
||||
}
|
||||
|
||||
// Re-copy from embedded source
|
||||
|
|
@ -488,6 +636,12 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
|
||||
McpLog.Info($"Server rebuilt successfully at {destRoot} (version {embeddedVer})");
|
||||
|
||||
// Clear any previous installation error
|
||||
|
||||
PackageLifecycleManager.ClearLastInstallError();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -747,13 +901,9 @@ namespace MCPForUnity.Editor.Helpers
|
|||
// Delete old installation
|
||||
if (Directory.Exists(destRoot))
|
||||
{
|
||||
try
|
||||
if (!DeleteDirectoryWithRetry(destRoot, maxRetries: 5, delayMs: 1000))
|
||||
{
|
||||
Directory.Delete(destRoot, recursive: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Warn($"Could not fully delete old server: {ex.Message}");
|
||||
McpLog.Warn($"Could not fully delete old server (files may be in use)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -803,9 +953,12 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
finally
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
if (File.Exists(tempZip)) File.Delete(tempZip);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Warn($"Could not delete temp zip file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for Vector3 operations
|
||||
/// </summary>
|
||||
public static class Vector3Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a JArray into a Vector3
|
||||
/// </summary>
|
||||
/// <param name="array">The array containing x, y, z coordinates</param>
|
||||
/// <returns>A Vector3 with the parsed coordinates</returns>
|
||||
/// <exception cref="System.Exception">Thrown when array is invalid</exception>
|
||||
public static Vector3 ParseVector3(JArray array)
|
||||
{
|
||||
if (array == null || array.Count != 3)
|
||||
throw new System.Exception("Vector3 must be an array of 3 floats [x, y, z].");
|
||||
return new Vector3((float)array[0], (float)array[1], (float)array[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f8514fd42f23cb641a36e52550825b35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Setup;
|
||||
using MCPForUnity.Editor.Windows;
|
||||
using UnityEditor;
|
||||
|
||||
namespace MCPForUnity.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Centralized menu items for MCP For Unity
|
||||
/// </summary>
|
||||
public static class MCPForUnityMenu
|
||||
{
|
||||
// ========================================
|
||||
// Main Menu Items
|
||||
// ========================================
|
||||
|
||||
/// <summary>
|
||||
/// Show the setup wizard
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Setup Wizard", priority = 1)]
|
||||
public static void ShowSetupWizard()
|
||||
{
|
||||
SetupWizard.ShowSetupWizard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the main MCP For Unity window
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Open MCP Window %#m", priority = 2)]
|
||||
public static void OpenMCPWindow()
|
||||
{
|
||||
MCPForUnityEditorWindow.ShowWindow();
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Tool Sync Menu Items
|
||||
// ========================================
|
||||
|
||||
/// <summary>
|
||||
/// Reimport all Python files in the project
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Reimport Python Files", priority = 99)]
|
||||
public static void ReimportPythonFiles()
|
||||
{
|
||||
PythonToolSyncProcessor.ReimportPythonFiles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually sync Python tools to the MCP server
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Sync Python Tools", priority = 100)]
|
||||
public static void SyncPythonTools()
|
||||
{
|
||||
PythonToolSyncProcessor.ManualSync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle auto-sync for Python tools
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Auto-Sync Python Tools", priority = 101)]
|
||||
public static void ToggleAutoSync()
|
||||
{
|
||||
PythonToolSyncProcessor.ToggleAutoSync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate menu item (shows checkmark when auto-sync is enabled)
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Tool Sync/Auto-Sync Python Tools", true, priority = 101)]
|
||||
public static bool ToggleAutoSyncValidate()
|
||||
{
|
||||
return PythonToolSyncProcessor.ToggleAutoSyncValidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de8f5721c34f7194392e9d8c7d0226c0
|
||||
guid: 42b27c415aa084fe6a9cc6cf03979d36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MCPForUnity.Editor.Models
|
||||
{
|
||||
[Serializable]
|
||||
public class ServerConfig
|
||||
{
|
||||
[JsonProperty("unity_host")]
|
||||
public string unityHost = "localhost";
|
||||
|
||||
[JsonProperty("unity_port")]
|
||||
public int unityPort;
|
||||
|
||||
[JsonProperty("mcp_port")]
|
||||
public int mcpPort;
|
||||
|
||||
[JsonProperty("connection_timeout")]
|
||||
public float connectionTimeout;
|
||||
|
||||
[JsonProperty("buffer_size")]
|
||||
public int bufferSize;
|
||||
|
||||
[JsonProperty("log_level")]
|
||||
public string logLevel;
|
||||
|
||||
[JsonProperty("log_format")]
|
||||
public string logFormat;
|
||||
|
||||
[JsonProperty("max_retries")]
|
||||
public int maxRetries;
|
||||
|
||||
[JsonProperty("retry_delay")]
|
||||
public float retryDelay;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e4e45386fcc282249907c2e3c7e5d9c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -176,9 +176,9 @@ namespace MCPForUnity.Editor.Services
|
|||
|
||||
if (configExists)
|
||||
{
|
||||
string configuredDir = McpConfigFileHelper.ExtractDirectoryArg(args);
|
||||
string configuredDir = McpConfigurationHelper.ExtractDirectoryArg(args);
|
||||
bool matches = !string.IsNullOrEmpty(configuredDir) &&
|
||||
McpConfigFileHelper.PathsEqual(configuredDir, pythonDir);
|
||||
McpConfigurationHelper.PathsEqual(configuredDir, pythonDir);
|
||||
|
||||
if (matches)
|
||||
{
|
||||
|
|
@ -396,7 +396,7 @@ namespace MCPForUnity.Editor.Services
|
|||
if (client.mcpType == McpTypes.Codex)
|
||||
{
|
||||
return CodexConfigHelper.BuildCodexServerBlock(uvPath,
|
||||
McpConfigFileHelper.ResolveServerDirectory(pythonDir, null));
|
||||
McpConfigurationHelper.ResolveServerDirectory(pythonDir, null));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
namespace MCPForUnity.Editor.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for platform detection and platform-specific environment access
|
||||
/// </summary>
|
||||
public interface IPlatformService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the current platform is Windows
|
||||
/// </summary>
|
||||
/// <returns>True if running on Windows</returns>
|
||||
bool IsWindows();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SystemRoot environment variable (Windows-specific)
|
||||
/// </summary>
|
||||
/// <returns>SystemRoot path, or null if not available</returns>
|
||||
string GetSystemRoot();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 19e6eaa637484e9fa19f9a0459809de2
|
||||
guid: 1d90ff7f9a1e84c9bbbbedee2f7eda2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -14,6 +14,7 @@ namespace MCPForUnity.Editor.Services
|
|||
private static ITestRunnerService _testRunnerService;
|
||||
private static IToolSyncService _toolSyncService;
|
||||
private static IPackageUpdateService _packageUpdateService;
|
||||
private static IPlatformService _platformService;
|
||||
|
||||
public static IBridgeControlService Bridge => _bridgeService ??= new BridgeControlService();
|
||||
public static IClientConfigurationService Client => _clientService ??= new ClientConfigurationService();
|
||||
|
|
@ -22,6 +23,7 @@ namespace MCPForUnity.Editor.Services
|
|||
public static ITestRunnerService Tests => _testRunnerService ??= new TestRunnerService();
|
||||
public static IToolSyncService ToolSync => _toolSyncService ??= new ToolSyncService();
|
||||
public static IPackageUpdateService Updates => _packageUpdateService ??= new PackageUpdateService();
|
||||
public static IPlatformService Platform => _platformService ??= new PlatformService();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a custom implementation for a service (useful for testing)
|
||||
|
|
@ -44,6 +46,8 @@ namespace MCPForUnity.Editor.Services
|
|||
_toolSyncService = ts;
|
||||
else if (implementation is IPackageUpdateService pu)
|
||||
_packageUpdateService = pu;
|
||||
else if (implementation is IPlatformService ps)
|
||||
_platformService = ps;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -58,6 +62,7 @@ namespace MCPForUnity.Editor.Services
|
|||
(_testRunnerService as IDisposable)?.Dispose();
|
||||
(_toolSyncService as IDisposable)?.Dispose();
|
||||
(_packageUpdateService as IDisposable)?.Dispose();
|
||||
(_platformService as IDisposable)?.Dispose();
|
||||
|
||||
_bridgeService = null;
|
||||
_clientService = null;
|
||||
|
|
@ -66,6 +71,7 @@ namespace MCPForUnity.Editor.Services
|
|||
_testRunnerService = null;
|
||||
_toolSyncService = null;
|
||||
_packageUpdateService = null;
|
||||
_platformService = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace MCPForUnity.Editor.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of platform detection service
|
||||
/// </summary>
|
||||
public class PlatformService : IPlatformService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the current platform is Windows
|
||||
/// </summary>
|
||||
/// <returns>True if running on Windows</returns>
|
||||
public bool IsWindows()
|
||||
{
|
||||
return Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SystemRoot environment variable (Windows-specific)
|
||||
/// </summary>
|
||||
/// <returns>SystemRoot path, or "C:\\Windows" as fallback on Windows, null on other platforms</returns>
|
||||
public string GetSystemRoot()
|
||||
{
|
||||
if (!IsWindows())
|
||||
return null;
|
||||
|
||||
return Environment.GetEnvironmentVariable("SystemRoot") ?? "C:\\Windows";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f69ad468942b74c0ea24e3e8e5f21a4b
|
||||
guid: 3b2d7f32a595c45dd8c01f141c69761c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
@ -97,63 +97,5 @@ namespace MCPForUnity.Editor.Setup
|
|||
McpLog.Info("Setup marked as dismissed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force show setup wizard (for manual invocation)
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Setup Wizard", priority = 1)]
|
||||
public static void ShowSetupWizardManual()
|
||||
{
|
||||
ShowSetupWizard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check dependencies and show status
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Check Dependencies", priority = 3)]
|
||||
public static void CheckDependencies()
|
||||
{
|
||||
var result = DependencyManager.CheckAllDependencies();
|
||||
|
||||
if (!result.IsSystemReady)
|
||||
{
|
||||
bool showWizard = EditorUtility.DisplayDialog(
|
||||
"MCP for Unity - Dependencies",
|
||||
$"System Status: {result.Summary}\n\nWould you like to open the Setup Wizard?",
|
||||
"Open Setup Wizard",
|
||||
"Close"
|
||||
);
|
||||
|
||||
if (showWizard)
|
||||
{
|
||||
ShowSetupWizard(result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"MCP for Unity - Dependencies",
|
||||
"✓ All dependencies are available and ready!\n\nMCP for Unity is ready to use.",
|
||||
"OK"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open MCP Client Configuration window
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Open MCP Window %#m", priority = 4)]
|
||||
public static void OpenClientConfiguration()
|
||||
{
|
||||
Windows.MCPForUnityEditorWindowNew.ShowWindow();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open legacy MCP Client Configuration window
|
||||
/// </summary>
|
||||
[MenuItem("Window/MCP For Unity/Open Legacy MCP Window", priority = 5)]
|
||||
public static void OpenLegacyClientConfiguration()
|
||||
{
|
||||
Windows.MCPForUnityEditorWindow.ShowWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@ namespace MCPForUnity.Editor.Setup
|
|||
private DependencyCheckResult _dependencyResult;
|
||||
private Vector2 _scrollPosition;
|
||||
private int _currentStep = 0;
|
||||
private McpClients _mcpClients;
|
||||
private int _selectedClientIndex = 0;
|
||||
|
||||
private readonly string[] _stepTitles = {
|
||||
"Setup",
|
||||
"Configure",
|
||||
"Complete"
|
||||
};
|
||||
|
||||
|
|
@ -42,14 +39,6 @@ namespace MCPForUnity.Editor.Setup
|
|||
{
|
||||
_dependencyResult = DependencyManager.CheckAllDependencies();
|
||||
}
|
||||
|
||||
_mcpClients = new McpClients();
|
||||
|
||||
// Check client configurations on startup
|
||||
foreach (var client in _mcpClients.clients)
|
||||
{
|
||||
CheckClientConfiguration(client);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
|
|
@ -62,8 +51,7 @@ namespace MCPForUnity.Editor.Setup
|
|||
switch (_currentStep)
|
||||
{
|
||||
case 0: DrawSetupStep(); break;
|
||||
case 1: DrawConfigureStep(); break;
|
||||
case 2: DrawCompleteStep(); break;
|
||||
case 1: DrawCompleteStep(); break;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
|
@ -132,7 +120,7 @@ namespace MCPForUnity.Editor.Setup
|
|||
{
|
||||
// Only show critical warnings when dependencies are actually missing
|
||||
EditorGUILayout.HelpBox(
|
||||
"⚠️ Missing Dependencies: MCP for Unity requires Python 3.10+ and UV package manager to function properly.",
|
||||
"\u26A0 Missing Dependencies: MCP for Unity requires Python 3.11+ and UV package manager to function properly.",
|
||||
MessageType.Warning
|
||||
);
|
||||
|
||||
|
|
@ -157,8 +145,6 @@ namespace MCPForUnity.Editor.Setup
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void DrawCompleteStep()
|
||||
{
|
||||
DrawSectionTitle("Setup Complete");
|
||||
|
|
@ -273,85 +259,6 @@ namespace MCPForUnity.Editor.Setup
|
|||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawConfigureStep()
|
||||
{
|
||||
DrawSectionTitle("AI Client Configuration");
|
||||
|
||||
// Check dependencies first (with caching to avoid heavy operations on every repaint)
|
||||
if (_dependencyResult == null || (DateTime.UtcNow - _dependencyResult.CheckedAt).TotalSeconds > 2)
|
||||
{
|
||||
_dependencyResult = DependencyManager.CheckAllDependencies();
|
||||
}
|
||||
if (!_dependencyResult.IsSystemReady)
|
||||
{
|
||||
DrawErrorStatus("Cannot Configure - System Requirements Not Met");
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"Client configuration requires system dependencies to be installed first. Please complete setup before proceeding.",
|
||||
MessageType.Warning
|
||||
);
|
||||
|
||||
if (GUILayout.Button("Go Back to Setup", GUILayout.Height(30)))
|
||||
{
|
||||
_currentStep = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
"Configure your AI assistants to work with Unity. Select a client below to set it up:",
|
||||
EditorStyles.wordWrappedLabel
|
||||
);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Client selection and configuration
|
||||
if (_mcpClients.clients.Count > 0)
|
||||
{
|
||||
// Client selector dropdown
|
||||
string[] clientNames = _mcpClients.clients.Select(c => c.name).ToArray();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
_selectedClientIndex = EditorGUILayout.Popup("Select AI Client:", _selectedClientIndex, clientNames);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
_selectedClientIndex = Mathf.Clamp(_selectedClientIndex, 0, _mcpClients.clients.Count - 1);
|
||||
// Refresh client status when selection changes
|
||||
CheckClientConfiguration(_mcpClients.clients[_selectedClientIndex]);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
var selectedClient = _mcpClients.clients[_selectedClientIndex];
|
||||
DrawClientConfigurationInWizard(selectedClient);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Batch configuration option
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.LabelField("Quick Setup", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField(
|
||||
"Automatically configure all detected AI clients at once:",
|
||||
EditorStyles.wordWrappedLabel
|
||||
);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (GUILayout.Button("Configure All Detected Clients", GUILayout.Height(30)))
|
||||
{
|
||||
ConfigureAllClientsInWizard();
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No AI clients detected. Make sure you have Claude Code, Cursor, or VSCode installed.", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.HelpBox(
|
||||
"💡 You might need to restart your AI client after configuring.",
|
||||
MessageType.Info
|
||||
);
|
||||
}
|
||||
|
||||
private void DrawFooter()
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
|
|
@ -371,7 +278,7 @@ namespace MCPForUnity.Editor.Setup
|
|||
{
|
||||
bool dismiss = EditorUtility.DisplayDialog(
|
||||
"Skip Setup",
|
||||
"⚠️ Skipping setup will leave MCP for Unity non-functional!\n\n" +
|
||||
"\u26A0 Skipping setup will leave MCP for Unity non-functional!\n\n" +
|
||||
"You can restart setup from: Window > MCP for Unity > Setup Wizard (Required)",
|
||||
"Skip Anyway",
|
||||
"Cancel"
|
||||
|
|
@ -405,295 +312,6 @@ namespace MCPForUnity.Editor.Setup
|
|||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawClientConfigurationInWizard(McpClient client)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
EditorGUILayout.LabelField($"{client.name} Configuration", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Show current status
|
||||
var statusColor = GetClientStatusColor(client);
|
||||
var originalColor = GUI.color;
|
||||
GUI.color = statusColor;
|
||||
EditorGUILayout.LabelField($"Status: {client.configStatus}", EditorStyles.label);
|
||||
GUI.color = originalColor;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Configuration buttons
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
if (client.mcpType == McpTypes.ClaudeCode)
|
||||
{
|
||||
// Special handling for Claude Code
|
||||
bool claudeAvailable = !string.IsNullOrEmpty(ExecPath.ResolveClaude());
|
||||
if (claudeAvailable)
|
||||
{
|
||||
bool isConfigured = client.status == McpStatus.Configured;
|
||||
string buttonText = isConfigured ? "Unregister" : "Register";
|
||||
if (GUILayout.Button($"{buttonText} with Claude Code"))
|
||||
{
|
||||
if (isConfigured)
|
||||
{
|
||||
UnregisterFromClaudeCode(client);
|
||||
}
|
||||
else
|
||||
{
|
||||
RegisterWithClaudeCode(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Claude Code not found. Please install Claude Code first.", MessageType.Warning);
|
||||
if (GUILayout.Button("Open Claude Code Website"))
|
||||
{
|
||||
Application.OpenURL("https://claude.ai/download");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard client configuration
|
||||
if (GUILayout.Button($"Configure {client.name}"))
|
||||
{
|
||||
ConfigureClientInWizard(client);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Manual Setup"))
|
||||
{
|
||||
ShowManualSetupInWizard(client);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private Color GetClientStatusColor(McpClient client)
|
||||
{
|
||||
return client.status switch
|
||||
{
|
||||
McpStatus.Configured => Color.green,
|
||||
McpStatus.Running => Color.green,
|
||||
McpStatus.Connected => Color.green,
|
||||
McpStatus.IncorrectPath => Color.yellow,
|
||||
McpStatus.CommunicationError => Color.yellow,
|
||||
McpStatus.NoResponse => Color.yellow,
|
||||
_ => Color.red
|
||||
};
|
||||
}
|
||||
|
||||
private void ConfigureClientInWizard(McpClient client)
|
||||
{
|
||||
try
|
||||
{
|
||||
string result = PerformClientConfiguration(client);
|
||||
|
||||
EditorUtility.DisplayDialog(
|
||||
$"{client.name} Configuration",
|
||||
result,
|
||||
"OK"
|
||||
);
|
||||
|
||||
// Refresh client status
|
||||
CheckClientConfiguration(client);
|
||||
Repaint();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Configuration Error",
|
||||
$"Failed to configure {client.name}: {ex.Message}",
|
||||
"OK"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureAllClientsInWizard()
|
||||
{
|
||||
int successCount = 0;
|
||||
int totalCount = _mcpClients.clients.Count;
|
||||
|
||||
foreach (var client in _mcpClients.clients)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (client.mcpType == McpTypes.ClaudeCode)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ExecPath.ResolveClaude()) && client.status != McpStatus.Configured)
|
||||
{
|
||||
RegisterWithClaudeCode(client);
|
||||
successCount++;
|
||||
}
|
||||
else if (client.status == McpStatus.Configured)
|
||||
{
|
||||
successCount++; // Already configured
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string result = PerformClientConfiguration(client);
|
||||
if (result.Contains("success", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
CheckClientConfiguration(client);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
McpLog.Error($"Failed to configure {client.name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog(
|
||||
"Batch Configuration Complete",
|
||||
$"Successfully configured {successCount} out of {totalCount} clients.\n\n" +
|
||||
"Restart your AI clients for changes to take effect.",
|
||||
"OK"
|
||||
);
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void RegisterWithClaudeCode(McpClient client)
|
||||
{
|
||||
try
|
||||
{
|
||||
string pythonDir = McpPathResolver.FindPackagePythonDirectory();
|
||||
string claudePath = ExecPath.ResolveClaude();
|
||||
string uvPath = ExecPath.ResolveUv() ?? "uv";
|
||||
|
||||
string args = $"mcp add UnityMCP -- \"{uvPath}\" run --directory \"{pythonDir}\" server.py";
|
||||
|
||||
if (!ExecPath.TryRun(claudePath, args, null, out var stdout, out var stderr, 15000, McpPathResolver.GetPathPrepend()))
|
||||
{
|
||||
if ((stdout + stderr).Contains("already exists", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
CheckClientConfiguration(client);
|
||||
EditorUtility.DisplayDialog("Claude Code", "MCP for Unity is already registered with Claude Code.", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception($"Registration failed: {stderr}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckClientConfiguration(client);
|
||||
EditorUtility.DisplayDialog("Claude Code", "Successfully registered MCP for Unity with Claude Code!", "OK");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Registration Error", $"Failed to register with Claude Code: {ex.Message}", "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterFromClaudeCode(McpClient client)
|
||||
{
|
||||
try
|
||||
{
|
||||
string claudePath = ExecPath.ResolveClaude();
|
||||
if (ExecPath.TryRun(claudePath, "mcp remove UnityMCP", null, out var stdout, out var stderr, 10000, McpPathResolver.GetPathPrepend()))
|
||||
{
|
||||
CheckClientConfiguration(client);
|
||||
EditorUtility.DisplayDialog("Claude Code", "Successfully unregistered MCP for Unity from Claude Code.", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception($"Unregistration failed: {stderr}");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Unregistration Error", $"Failed to unregister from Claude Code: {ex.Message}", "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private string PerformClientConfiguration(McpClient client)
|
||||
{
|
||||
// This mirrors the logic from MCPForUnityEditorWindow.ConfigureMcpClient
|
||||
string configPath = McpConfigurationHelper.GetClientConfigPath(client);
|
||||
string pythonDir = McpPathResolver.FindPackagePythonDirectory();
|
||||
|
||||
if (string.IsNullOrEmpty(pythonDir))
|
||||
{
|
||||
return "Manual configuration required - Python server directory not found.";
|
||||
}
|
||||
|
||||
McpConfigurationHelper.EnsureConfigDirectoryExists(configPath);
|
||||
return McpConfigurationHelper.WriteMcpConfiguration(pythonDir, configPath, client);
|
||||
}
|
||||
|
||||
private void ShowManualSetupInWizard(McpClient client)
|
||||
{
|
||||
string configPath = McpConfigurationHelper.GetClientConfigPath(client);
|
||||
string pythonDir = McpPathResolver.FindPackagePythonDirectory();
|
||||
string uvPath = ServerInstaller.FindUvPath();
|
||||
|
||||
if (string.IsNullOrEmpty(uvPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Manual Setup", "UV package manager not found. Please install UV first.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build manual configuration using the sophisticated helper logic
|
||||
string result = McpConfigurationHelper.WriteMcpConfiguration(pythonDir, configPath, client);
|
||||
string manualConfig;
|
||||
|
||||
if (result == "Configured successfully")
|
||||
{
|
||||
// Read back the configuration that was written
|
||||
try
|
||||
{
|
||||
manualConfig = System.IO.File.ReadAllText(configPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
manualConfig = "Configuration written successfully, but could not read back for display.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
manualConfig = $"Configuration failed: {result}";
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog(
|
||||
$"Manual Setup - {client.name}",
|
||||
$"Configuration file location:\n{configPath}\n\n" +
|
||||
$"Configuration result:\n{manualConfig}",
|
||||
"OK"
|
||||
);
|
||||
}
|
||||
|
||||
private void CheckClientConfiguration(McpClient client)
|
||||
{
|
||||
// Basic status check - could be enhanced to mirror MCPForUnityEditorWindow logic
|
||||
try
|
||||
{
|
||||
string configPath = McpConfigurationHelper.GetClientConfigPath(client);
|
||||
if (System.IO.File.Exists(configPath))
|
||||
{
|
||||
client.configStatus = "Configured";
|
||||
client.status = McpStatus.Configured;
|
||||
}
|
||||
else
|
||||
{
|
||||
client.configStatus = "Not Configured";
|
||||
client.status = McpStatus.NotConfigured;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
client.configStatus = "Error";
|
||||
client.status = McpStatus.Error;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenInstallationUrls()
|
||||
{
|
||||
var (pythonUrl, uvUrl) = DependencyManager.GetInstallationUrls();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f740bec3a8d04716adeab35c412a15f
|
||||
guid: 9f0c6e1d3e4d5e6f7a8b9c0d1e2f3a4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
|
|
|
|||
|
|
@ -1,860 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements; // For Unity 2021 compatibility
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using MCPForUnity.Editor.Data;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Models;
|
||||
using MCPForUnity.Editor.Services;
|
||||
|
||||
namespace MCPForUnity.Editor.Windows
|
||||
{
|
||||
public class MCPForUnityEditorWindowNew : EditorWindow
|
||||
{
|
||||
// Protocol enum for future HTTP support
|
||||
private enum ConnectionProtocol
|
||||
{
|
||||
Stdio,
|
||||
// HTTPStreaming // Future
|
||||
}
|
||||
|
||||
// Settings UI Elements
|
||||
private Label versionLabel;
|
||||
private Toggle debugLogsToggle;
|
||||
private EnumField validationLevelField;
|
||||
private Label validationDescription;
|
||||
private Foldout advancedSettingsFoldout;
|
||||
private TextField mcpServerPathOverride;
|
||||
private TextField uvPathOverride;
|
||||
private Button browsePythonButton;
|
||||
private Button clearPythonButton;
|
||||
private Button browseUvButton;
|
||||
private Button clearUvButton;
|
||||
private VisualElement mcpServerPathStatus;
|
||||
private VisualElement uvPathStatus;
|
||||
|
||||
// Connection UI Elements
|
||||
private EnumField protocolDropdown;
|
||||
private TextField unityPortField;
|
||||
private TextField serverPortField;
|
||||
private VisualElement statusIndicator;
|
||||
private Label connectionStatusLabel;
|
||||
private Button connectionToggleButton;
|
||||
private VisualElement healthIndicator;
|
||||
private Label healthStatusLabel;
|
||||
private Button testConnectionButton;
|
||||
private VisualElement serverStatusBanner;
|
||||
private Label serverStatusMessage;
|
||||
private Button downloadServerButton;
|
||||
private Button rebuildServerButton;
|
||||
|
||||
// Client UI Elements
|
||||
private DropdownField clientDropdown;
|
||||
private Button configureAllButton;
|
||||
private VisualElement clientStatusIndicator;
|
||||
private Label clientStatusLabel;
|
||||
private Button configureButton;
|
||||
private VisualElement claudeCliPathRow;
|
||||
private TextField claudeCliPath;
|
||||
private Button browseClaudeButton;
|
||||
private Foldout manualConfigFoldout;
|
||||
private TextField configPathField;
|
||||
private Button copyPathButton;
|
||||
private Button openFileButton;
|
||||
private TextField configJsonField;
|
||||
private Button copyJsonButton;
|
||||
private Label installationStepsLabel;
|
||||
|
||||
// Data
|
||||
private readonly McpClients mcpClients = new();
|
||||
private int selectedClientIndex = 0;
|
||||
private ValidationLevel currentValidationLevel = ValidationLevel.Standard;
|
||||
|
||||
// Validation levels matching the existing enum
|
||||
private enum ValidationLevel
|
||||
{
|
||||
Basic,
|
||||
Standard,
|
||||
Comprehensive,
|
||||
Strict
|
||||
}
|
||||
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<MCPForUnityEditorWindowNew>("MCP For Unity");
|
||||
window.minSize = new Vector2(500, 600);
|
||||
}
|
||||
public void CreateGUI()
|
||||
{
|
||||
// Determine base path (Package Manager vs Asset Store install)
|
||||
string basePath = AssetPathUtility.GetMcpPackageRootPath();
|
||||
|
||||
// Load UXML
|
||||
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
|
||||
$"{basePath}/Editor/Windows/MCPForUnityEditorWindowNew.uxml"
|
||||
);
|
||||
|
||||
if (visualTree == null)
|
||||
{
|
||||
McpLog.Error($"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindowNew.uxml");
|
||||
return;
|
||||
}
|
||||
|
||||
visualTree.CloneTree(rootVisualElement);
|
||||
|
||||
// Load USS
|
||||
var styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(
|
||||
$"{basePath}/Editor/Windows/MCPForUnityEditorWindowNew.uss"
|
||||
);
|
||||
|
||||
if (styleSheet != null)
|
||||
{
|
||||
rootVisualElement.styleSheets.Add(styleSheet);
|
||||
}
|
||||
|
||||
// Cache UI elements
|
||||
CacheUIElements();
|
||||
|
||||
// Initialize UI
|
||||
InitializeUI();
|
||||
|
||||
// Register callbacks
|
||||
RegisterCallbacks();
|
||||
|
||||
// Initial update
|
||||
UpdateConnectionStatus();
|
||||
UpdateServerStatusBanner();
|
||||
UpdateClientStatus();
|
||||
UpdatePathOverrides();
|
||||
// Technically not required to connect, but if we don't do this, the UI will be blank
|
||||
UpdateManualConfiguration();
|
||||
UpdateClaudeCliPathVisibility();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
EditorApplication.update += OnEditorUpdate;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorApplication.update -= OnEditorUpdate;
|
||||
}
|
||||
|
||||
private void OnFocus()
|
||||
{
|
||||
// Only refresh data if UI is built
|
||||
if (rootVisualElement == null || rootVisualElement.childCount == 0)
|
||||
return;
|
||||
|
||||
RefreshAllData();
|
||||
}
|
||||
|
||||
private void OnEditorUpdate()
|
||||
{
|
||||
// Only update UI if it's built
|
||||
if (rootVisualElement == null || rootVisualElement.childCount == 0)
|
||||
return;
|
||||
|
||||
UpdateConnectionStatus();
|
||||
}
|
||||
|
||||
private void RefreshAllData()
|
||||
{
|
||||
// Update connection status
|
||||
UpdateConnectionStatus();
|
||||
|
||||
// Auto-verify bridge health if connected
|
||||
if (MCPServiceLocator.Bridge.IsRunning)
|
||||
{
|
||||
VerifyBridgeConnection();
|
||||
}
|
||||
|
||||
// Update path overrides
|
||||
UpdatePathOverrides();
|
||||
|
||||
// Refresh selected client (may have been configured externally)
|
||||
if (selectedClientIndex >= 0 && selectedClientIndex < mcpClients.clients.Count)
|
||||
{
|
||||
var client = mcpClients.clients[selectedClientIndex];
|
||||
MCPServiceLocator.Client.CheckClientStatus(client);
|
||||
UpdateClientStatus();
|
||||
UpdateManualConfiguration();
|
||||
UpdateClaudeCliPathVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheUIElements()
|
||||
{
|
||||
// Settings
|
||||
versionLabel = rootVisualElement.Q<Label>("version-label");
|
||||
debugLogsToggle = rootVisualElement.Q<Toggle>("debug-logs-toggle");
|
||||
validationLevelField = rootVisualElement.Q<EnumField>("validation-level");
|
||||
validationDescription = rootVisualElement.Q<Label>("validation-description");
|
||||
advancedSettingsFoldout = rootVisualElement.Q<Foldout>("advanced-settings-foldout");
|
||||
mcpServerPathOverride = rootVisualElement.Q<TextField>("python-path-override");
|
||||
uvPathOverride = rootVisualElement.Q<TextField>("uv-path-override");
|
||||
browsePythonButton = rootVisualElement.Q<Button>("browse-python-button");
|
||||
clearPythonButton = rootVisualElement.Q<Button>("clear-python-button");
|
||||
browseUvButton = rootVisualElement.Q<Button>("browse-uv-button");
|
||||
clearUvButton = rootVisualElement.Q<Button>("clear-uv-button");
|
||||
mcpServerPathStatus = rootVisualElement.Q<VisualElement>("mcp-server-path-status");
|
||||
uvPathStatus = rootVisualElement.Q<VisualElement>("uv-path-status");
|
||||
|
||||
// Connection
|
||||
protocolDropdown = rootVisualElement.Q<EnumField>("protocol-dropdown");
|
||||
unityPortField = rootVisualElement.Q<TextField>("unity-port");
|
||||
serverPortField = rootVisualElement.Q<TextField>("server-port");
|
||||
statusIndicator = rootVisualElement.Q<VisualElement>("status-indicator");
|
||||
connectionStatusLabel = rootVisualElement.Q<Label>("connection-status");
|
||||
connectionToggleButton = rootVisualElement.Q<Button>("connection-toggle");
|
||||
healthIndicator = rootVisualElement.Q<VisualElement>("health-indicator");
|
||||
healthStatusLabel = rootVisualElement.Q<Label>("health-status");
|
||||
testConnectionButton = rootVisualElement.Q<Button>("test-connection-button");
|
||||
serverStatusBanner = rootVisualElement.Q<VisualElement>("server-status-banner");
|
||||
serverStatusMessage = rootVisualElement.Q<Label>("server-status-message");
|
||||
downloadServerButton = rootVisualElement.Q<Button>("download-server-button");
|
||||
rebuildServerButton = rootVisualElement.Q<Button>("rebuild-server-button");
|
||||
|
||||
// Client
|
||||
clientDropdown = rootVisualElement.Q<DropdownField>("client-dropdown");
|
||||
configureAllButton = rootVisualElement.Q<Button>("configure-all-button");
|
||||
clientStatusIndicator = rootVisualElement.Q<VisualElement>("client-status-indicator");
|
||||
clientStatusLabel = rootVisualElement.Q<Label>("client-status");
|
||||
configureButton = rootVisualElement.Q<Button>("configure-button");
|
||||
claudeCliPathRow = rootVisualElement.Q<VisualElement>("claude-cli-path-row");
|
||||
claudeCliPath = rootVisualElement.Q<TextField>("claude-cli-path");
|
||||
browseClaudeButton = rootVisualElement.Q<Button>("browse-claude-button");
|
||||
manualConfigFoldout = rootVisualElement.Q<Foldout>("manual-config-foldout");
|
||||
configPathField = rootVisualElement.Q<TextField>("config-path");
|
||||
copyPathButton = rootVisualElement.Q<Button>("copy-path-button");
|
||||
openFileButton = rootVisualElement.Q<Button>("open-file-button");
|
||||
configJsonField = rootVisualElement.Q<TextField>("config-json");
|
||||
copyJsonButton = rootVisualElement.Q<Button>("copy-json-button");
|
||||
installationStepsLabel = rootVisualElement.Q<Label>("installation-steps");
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
// Settings Section
|
||||
UpdateVersionLabel();
|
||||
debugLogsToggle.value = EditorPrefs.GetBool("MCPForUnity.DebugLogs", false);
|
||||
|
||||
validationLevelField.Init(ValidationLevel.Standard);
|
||||
int savedLevel = EditorPrefs.GetInt("MCPForUnity.ValidationLevel", 1);
|
||||
currentValidationLevel = (ValidationLevel)Mathf.Clamp(savedLevel, 0, 3);
|
||||
validationLevelField.value = currentValidationLevel;
|
||||
UpdateValidationDescription();
|
||||
|
||||
// Advanced settings starts collapsed
|
||||
advancedSettingsFoldout.value = false;
|
||||
|
||||
// Connection Section
|
||||
protocolDropdown.Init(ConnectionProtocol.Stdio);
|
||||
protocolDropdown.SetEnabled(false); // Disabled for now, only stdio supported
|
||||
|
||||
unityPortField.value = MCPServiceLocator.Bridge.CurrentPort.ToString();
|
||||
serverPortField.value = "6500";
|
||||
|
||||
// Client Configuration
|
||||
var clientNames = mcpClients.clients.Select(c => c.name).ToList();
|
||||
clientDropdown.choices = clientNames;
|
||||
if (clientNames.Count > 0)
|
||||
{
|
||||
clientDropdown.index = 0;
|
||||
}
|
||||
|
||||
// Manual config starts collapsed
|
||||
manualConfigFoldout.value = false;
|
||||
|
||||
// Claude CLI path row hidden by default
|
||||
claudeCliPathRow.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
// Settings callbacks
|
||||
debugLogsToggle.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
EditorPrefs.SetBool("MCPForUnity.DebugLogs", evt.newValue);
|
||||
});
|
||||
|
||||
validationLevelField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
currentValidationLevel = (ValidationLevel)evt.newValue;
|
||||
EditorPrefs.SetInt("MCPForUnity.ValidationLevel", (int)currentValidationLevel);
|
||||
UpdateValidationDescription();
|
||||
});
|
||||
|
||||
// Advanced settings callbacks
|
||||
browsePythonButton.clicked += OnBrowsePythonClicked;
|
||||
clearPythonButton.clicked += OnClearPythonClicked;
|
||||
browseUvButton.clicked += OnBrowseUvClicked;
|
||||
clearUvButton.clicked += OnClearUvClicked;
|
||||
|
||||
// Connection callbacks
|
||||
connectionToggleButton.clicked += OnConnectionToggleClicked;
|
||||
testConnectionButton.clicked += OnTestConnectionClicked;
|
||||
downloadServerButton.clicked += OnDownloadServerClicked;
|
||||
rebuildServerButton.clicked += OnRebuildServerClicked;
|
||||
|
||||
// Client callbacks
|
||||
clientDropdown.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
selectedClientIndex = clientDropdown.index;
|
||||
UpdateClientStatus();
|
||||
UpdateManualConfiguration();
|
||||
UpdateClaudeCliPathVisibility();
|
||||
});
|
||||
|
||||
configureAllButton.clicked += OnConfigureAllClientsClicked;
|
||||
configureButton.clicked += OnConfigureClicked;
|
||||
browseClaudeButton.clicked += OnBrowseClaudeClicked;
|
||||
copyPathButton.clicked += OnCopyPathClicked;
|
||||
openFileButton.clicked += OnOpenFileClicked;
|
||||
copyJsonButton.clicked += OnCopyJsonClicked;
|
||||
}
|
||||
|
||||
private void UpdateValidationDescription()
|
||||
{
|
||||
validationDescription.text = GetValidationLevelDescription((int)currentValidationLevel);
|
||||
}
|
||||
|
||||
private string GetValidationLevelDescription(int index)
|
||||
{
|
||||
return index switch
|
||||
{
|
||||
0 => "Only basic syntax checks (braces, quotes, comments)",
|
||||
1 => "Syntax checks + Unity best practices and warnings",
|
||||
2 => "All checks + semantic analysis and performance warnings",
|
||||
3 => "Full semantic validation with namespace/type resolution (requires Roslyn)",
|
||||
_ => "Standard validation"
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateConnectionStatus()
|
||||
{
|
||||
var bridgeService = MCPServiceLocator.Bridge;
|
||||
bool isRunning = bridgeService.IsRunning;
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
connectionStatusLabel.text = "Connected";
|
||||
statusIndicator.RemoveFromClassList("disconnected");
|
||||
statusIndicator.AddToClassList("connected");
|
||||
connectionToggleButton.text = "Stop";
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionStatusLabel.text = "Disconnected";
|
||||
statusIndicator.RemoveFromClassList("connected");
|
||||
statusIndicator.AddToClassList("disconnected");
|
||||
connectionToggleButton.text = "Start";
|
||||
|
||||
// Reset health status when disconnected
|
||||
healthStatusLabel.text = "Unknown";
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("warning");
|
||||
healthIndicator.AddToClassList("unknown");
|
||||
}
|
||||
|
||||
// Update ports
|
||||
unityPortField.value = bridgeService.CurrentPort.ToString();
|
||||
}
|
||||
|
||||
private void UpdateClientStatus()
|
||||
{
|
||||
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
||||
return;
|
||||
|
||||
var client = mcpClients.clients[selectedClientIndex];
|
||||
MCPServiceLocator.Client.CheckClientStatus(client);
|
||||
|
||||
clientStatusLabel.text = client.GetStatusDisplayString();
|
||||
|
||||
// Reset inline color style (clear error state from OnConfigureClicked)
|
||||
clientStatusLabel.style.color = StyleKeyword.Null;
|
||||
|
||||
// Update status indicator color
|
||||
clientStatusIndicator.RemoveFromClassList("configured");
|
||||
clientStatusIndicator.RemoveFromClassList("not-configured");
|
||||
clientStatusIndicator.RemoveFromClassList("warning");
|
||||
|
||||
switch (client.status)
|
||||
{
|
||||
case McpStatus.Configured:
|
||||
case McpStatus.Running:
|
||||
case McpStatus.Connected:
|
||||
clientStatusIndicator.AddToClassList("configured");
|
||||
break;
|
||||
case McpStatus.IncorrectPath:
|
||||
case McpStatus.CommunicationError:
|
||||
case McpStatus.NoResponse:
|
||||
clientStatusIndicator.AddToClassList("warning");
|
||||
break;
|
||||
default:
|
||||
clientStatusIndicator.AddToClassList("not-configured");
|
||||
break;
|
||||
}
|
||||
|
||||
// Update configure button text for Claude Code
|
||||
if (client.mcpType == McpTypes.ClaudeCode)
|
||||
{
|
||||
bool isConfigured = client.status == McpStatus.Configured;
|
||||
configureButton.text = isConfigured ? "Unregister" : "Register";
|
||||
}
|
||||
else
|
||||
{
|
||||
configureButton.text = "Configure";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateManualConfiguration()
|
||||
{
|
||||
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
||||
return;
|
||||
|
||||
var client = mcpClients.clients[selectedClientIndex];
|
||||
|
||||
// Get config path
|
||||
string configPath = MCPServiceLocator.Client.GetConfigPath(client);
|
||||
configPathField.value = configPath;
|
||||
|
||||
// Get config JSON
|
||||
string configJson = MCPServiceLocator.Client.GenerateConfigJson(client);
|
||||
configJsonField.value = configJson;
|
||||
|
||||
// Get installation steps
|
||||
string steps = MCPServiceLocator.Client.GetInstallationSteps(client);
|
||||
installationStepsLabel.text = steps;
|
||||
}
|
||||
|
||||
private void UpdateClaudeCliPathVisibility()
|
||||
{
|
||||
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
||||
return;
|
||||
|
||||
var client = mcpClients.clients[selectedClientIndex];
|
||||
|
||||
// Show Claude CLI path only for Claude Code client
|
||||
if (client.mcpType == McpTypes.ClaudeCode)
|
||||
{
|
||||
string claudePath = MCPServiceLocator.Paths.GetClaudeCliPath();
|
||||
if (string.IsNullOrEmpty(claudePath))
|
||||
{
|
||||
// Show path selector if not found
|
||||
claudeCliPathRow.style.display = DisplayStyle.Flex;
|
||||
claudeCliPath.value = "Not found - click Browse to select";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show detected path
|
||||
claudeCliPathRow.style.display = DisplayStyle.Flex;
|
||||
claudeCliPath.value = claudePath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
claudeCliPathRow.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePathOverrides()
|
||||
{
|
||||
var pathService = MCPServiceLocator.Paths;
|
||||
|
||||
// MCP Server Path
|
||||
string mcpServerPath = pathService.GetMcpServerPath();
|
||||
if (pathService.HasMcpServerOverride)
|
||||
{
|
||||
mcpServerPathOverride.value = mcpServerPath ?? "(override set but invalid)";
|
||||
}
|
||||
else
|
||||
{
|
||||
mcpServerPathOverride.value = mcpServerPath ?? "(auto-detected)";
|
||||
}
|
||||
|
||||
// Update status indicator
|
||||
mcpServerPathStatus.RemoveFromClassList("valid");
|
||||
mcpServerPathStatus.RemoveFromClassList("invalid");
|
||||
if (!string.IsNullOrEmpty(mcpServerPath) && File.Exists(Path.Combine(mcpServerPath, "server.py")))
|
||||
{
|
||||
mcpServerPathStatus.AddToClassList("valid");
|
||||
}
|
||||
else
|
||||
{
|
||||
mcpServerPathStatus.AddToClassList("invalid");
|
||||
}
|
||||
|
||||
// UV Path
|
||||
string uvPath = pathService.GetUvPath();
|
||||
if (pathService.HasUvPathOverride)
|
||||
{
|
||||
uvPathOverride.value = uvPath ?? "(override set but invalid)";
|
||||
}
|
||||
else
|
||||
{
|
||||
uvPathOverride.value = uvPath ?? "(auto-detected)";
|
||||
}
|
||||
|
||||
// Update status indicator
|
||||
uvPathStatus.RemoveFromClassList("valid");
|
||||
uvPathStatus.RemoveFromClassList("invalid");
|
||||
if (!string.IsNullOrEmpty(uvPath) && File.Exists(uvPath))
|
||||
{
|
||||
uvPathStatus.AddToClassList("valid");
|
||||
}
|
||||
else
|
||||
{
|
||||
uvPathStatus.AddToClassList("invalid");
|
||||
}
|
||||
}
|
||||
|
||||
// Button callbacks
|
||||
private void OnConnectionToggleClicked()
|
||||
{
|
||||
var bridgeService = MCPServiceLocator.Bridge;
|
||||
|
||||
if (bridgeService.IsRunning)
|
||||
{
|
||||
bridgeService.Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
bridgeService.Start();
|
||||
|
||||
// Verify connection after starting (Option C: verify on connect)
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (bridgeService.IsRunning)
|
||||
{
|
||||
VerifyBridgeConnection();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
UpdateConnectionStatus();
|
||||
}
|
||||
|
||||
private void OnTestConnectionClicked()
|
||||
{
|
||||
VerifyBridgeConnection();
|
||||
}
|
||||
|
||||
private void VerifyBridgeConnection()
|
||||
{
|
||||
var bridgeService = MCPServiceLocator.Bridge;
|
||||
|
||||
if (!bridgeService.IsRunning)
|
||||
{
|
||||
healthStatusLabel.text = "Disconnected";
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("warning");
|
||||
healthIndicator.AddToClassList("unknown");
|
||||
McpLog.Warn("Cannot verify connection: Bridge is not running");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = bridgeService.Verify(bridgeService.CurrentPort);
|
||||
|
||||
healthIndicator.RemoveFromClassList("healthy");
|
||||
healthIndicator.RemoveFromClassList("warning");
|
||||
healthIndicator.RemoveFromClassList("unknown");
|
||||
|
||||
if (result.Success && result.PingSucceeded)
|
||||
{
|
||||
healthStatusLabel.text = "Healthy";
|
||||
healthIndicator.AddToClassList("healthy");
|
||||
McpLog.Info("Bridge verification successful");
|
||||
}
|
||||
else if (result.HandshakeValid)
|
||||
{
|
||||
healthStatusLabel.text = "Ping Failed";
|
||||
healthIndicator.AddToClassList("warning");
|
||||
McpLog.Warn($"Bridge verification warning: {result.Message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
healthStatusLabel.text = "Unhealthy";
|
||||
healthIndicator.AddToClassList("warning");
|
||||
McpLog.Error($"Bridge verification failed: {result.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDownloadServerClicked()
|
||||
{
|
||||
if (ServerInstaller.DownloadAndInstallServer())
|
||||
{
|
||||
UpdateServerStatusBanner();
|
||||
UpdatePathOverrides();
|
||||
EditorUtility.DisplayDialog(
|
||||
"Download Complete",
|
||||
"Server installed successfully! Start your connection and configure your MCP clients to begin.",
|
||||
"OK"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRebuildServerClicked()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool success = ServerInstaller.RebuildMcpServer();
|
||||
if (success)
|
||||
{
|
||||
EditorUtility.DisplayDialog("MCP For Unity", "Server rebuilt successfully.", "OK");
|
||||
UpdateServerStatusBanner();
|
||||
UpdatePathOverrides();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("MCP For Unity", "Rebuild failed. Please check Console for details.", "OK");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Error($"Failed to rebuild server: {ex.Message}");
|
||||
EditorUtility.DisplayDialog("MCP For Unity", $"Rebuild failed: {ex.Message}", "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateServerStatusBanner()
|
||||
{
|
||||
bool hasEmbedded = ServerInstaller.HasEmbeddedServer();
|
||||
string installedVer = ServerInstaller.GetInstalledServerVersion();
|
||||
string packageVer = AssetPathUtility.GetPackageVersion();
|
||||
|
||||
// Show/hide download vs rebuild buttons
|
||||
if (hasEmbedded)
|
||||
{
|
||||
downloadServerButton.style.display = DisplayStyle.None;
|
||||
rebuildServerButton.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadServerButton.style.display = DisplayStyle.Flex;
|
||||
rebuildServerButton.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
// Update banner
|
||||
if (!hasEmbedded && string.IsNullOrEmpty(installedVer))
|
||||
{
|
||||
serverStatusMessage.text = "\u26A0 Server not installed. Click 'Download & Install Server' to get started.";
|
||||
serverStatusBanner.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
else if (!hasEmbedded && !string.IsNullOrEmpty(installedVer) && installedVer != packageVer)
|
||||
{
|
||||
serverStatusMessage.text = $"\u26A0 Server update available (v{installedVer} \u2192 v{packageVer}). Update recommended.";
|
||||
serverStatusBanner.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverStatusBanner.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConfigureAllClientsClicked()
|
||||
{
|
||||
try
|
||||
{
|
||||
var summary = MCPServiceLocator.Client.ConfigureAllDetectedClients();
|
||||
|
||||
// Build detailed message
|
||||
string message = summary.GetSummaryMessage() + "\n\n";
|
||||
foreach (var msg in summary.Messages)
|
||||
{
|
||||
message += msg + "\n";
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog("Configure All Clients", message, "OK");
|
||||
|
||||
// Refresh current client status
|
||||
if (selectedClientIndex >= 0 && selectedClientIndex < mcpClients.clients.Count)
|
||||
{
|
||||
UpdateClientStatus();
|
||||
UpdateManualConfiguration();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Configuration Failed", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConfigureClicked()
|
||||
{
|
||||
if (selectedClientIndex < 0 || selectedClientIndex >= mcpClients.clients.Count)
|
||||
return;
|
||||
|
||||
var client = mcpClients.clients[selectedClientIndex];
|
||||
|
||||
try
|
||||
{
|
||||
if (client.mcpType == McpTypes.ClaudeCode)
|
||||
{
|
||||
bool isConfigured = client.status == McpStatus.Configured;
|
||||
if (isConfigured)
|
||||
{
|
||||
MCPServiceLocator.Client.UnregisterClaudeCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPServiceLocator.Client.RegisterClaudeCode();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MCPServiceLocator.Client.ConfigureClient(client);
|
||||
}
|
||||
|
||||
UpdateClientStatus();
|
||||
UpdateManualConfiguration();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
clientStatusLabel.text = "Error";
|
||||
clientStatusLabel.style.color = Color.red;
|
||||
McpLog.Error($"Configuration failed: {ex.Message}");
|
||||
EditorUtility.DisplayDialog("Configuration Failed", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBrowsePythonClicked()
|
||||
{
|
||||
string picked = EditorUtility.OpenFolderPanel("Select MCP Server Directory", Application.dataPath, "");
|
||||
if (!string.IsNullOrEmpty(picked))
|
||||
{
|
||||
try
|
||||
{
|
||||
MCPServiceLocator.Paths.SetMcpServerOverride(picked);
|
||||
UpdatePathOverrides();
|
||||
McpLog.Info($"MCP server path override set to: {picked}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Invalid Path", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClearPythonClicked()
|
||||
{
|
||||
MCPServiceLocator.Paths.ClearMcpServerOverride();
|
||||
UpdatePathOverrides();
|
||||
McpLog.Info("MCP server path override cleared");
|
||||
}
|
||||
|
||||
private void OnBrowseUvClicked()
|
||||
{
|
||||
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
|
||||
? "/opt/homebrew/bin"
|
||||
: Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
string picked = EditorUtility.OpenFilePanel("Select UV Executable", suggested, "");
|
||||
if (!string.IsNullOrEmpty(picked))
|
||||
{
|
||||
try
|
||||
{
|
||||
MCPServiceLocator.Paths.SetUvPathOverride(picked);
|
||||
UpdatePathOverrides();
|
||||
McpLog.Info($"UV path override set to: {picked}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Invalid Path", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClearUvClicked()
|
||||
{
|
||||
MCPServiceLocator.Paths.ClearUvPathOverride();
|
||||
UpdatePathOverrides();
|
||||
McpLog.Info("UV path override cleared");
|
||||
}
|
||||
|
||||
private void OnBrowseClaudeClicked()
|
||||
{
|
||||
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
|
||||
? "/opt/homebrew/bin"
|
||||
: Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
string picked = EditorUtility.OpenFilePanel("Select Claude CLI", suggested, "");
|
||||
if (!string.IsNullOrEmpty(picked))
|
||||
{
|
||||
try
|
||||
{
|
||||
MCPServiceLocator.Paths.SetClaudeCliPathOverride(picked);
|
||||
UpdateClaudeCliPathVisibility();
|
||||
UpdateClientStatus();
|
||||
McpLog.Info($"Claude CLI path override set to: {picked}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Invalid Path", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCopyPathClicked()
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = configPathField.value;
|
||||
McpLog.Info("Config path copied to clipboard");
|
||||
}
|
||||
|
||||
private void OnOpenFileClicked()
|
||||
{
|
||||
string path = configPathField.value;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Open File", "The configuration file path does not exist.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = path,
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Error($"Failed to open file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCopyJsonClicked()
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = configJsonField.value;
|
||||
McpLog.Info("Configuration copied to clipboard");
|
||||
}
|
||||
|
||||
private void UpdateVersionLabel()
|
||||
{
|
||||
string currentVersion = AssetPathUtility.GetPackageVersion();
|
||||
versionLabel.text = $"v{currentVersion}";
|
||||
|
||||
// Check for updates using the service
|
||||
var updateCheck = MCPServiceLocator.Updates.CheckForUpdate(currentVersion);
|
||||
|
||||
if (updateCheck.UpdateAvailable && !string.IsNullOrEmpty(updateCheck.LatestVersion))
|
||||
{
|
||||
// Update available - enhance the label
|
||||
versionLabel.text = $"\u2191 v{currentVersion} (Update available: v{updateCheck.LatestVersion})";
|
||||
versionLabel.style.color = new Color(1f, 0.7f, 0f); // Orange
|
||||
versionLabel.tooltip = $"Version {updateCheck.LatestVersion} is available. Update via Package Manager.\n\nGit URL: https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity";
|
||||
}
|
||||
else
|
||||
{
|
||||
versionLabel.style.color = StyleKeyword.Null; // Default color
|
||||
versionLabel.tooltip = $"Current version: {currentVersion}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f0c6e1d3e4d5e6f7a8b9c0d1e2f3a4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,303 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using MCPForUnity.Editor.Models;
|
||||
|
||||
namespace MCPForUnity.Editor.Windows
|
||||
{
|
||||
// Editor window to display manual configuration instructions
|
||||
public class ManualConfigEditorWindow : EditorWindow
|
||||
{
|
||||
protected string configPath;
|
||||
protected string configJson;
|
||||
protected Vector2 scrollPos;
|
||||
protected bool pathCopied = false;
|
||||
protected bool jsonCopied = false;
|
||||
protected float copyFeedbackTimer = 0;
|
||||
protected McpClient mcpClient;
|
||||
|
||||
public static void ShowWindow(string configPath, string configJson, McpClient mcpClient)
|
||||
{
|
||||
var window = GetWindow<ManualConfigEditorWindow>("Manual Configuration");
|
||||
window.configPath = configPath;
|
||||
window.configJson = configJson;
|
||||
window.mcpClient = mcpClient;
|
||||
window.minSize = new Vector2(500, 400);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
protected virtual void OnGUI()
|
||||
{
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
// Header with improved styling
|
||||
EditorGUILayout.Space(10);
|
||||
Rect titleRect = EditorGUILayout.GetControlRect(false, 30);
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(titleRect.x, titleRect.y, titleRect.width, titleRect.height),
|
||||
new Color(0.2f, 0.2f, 0.2f, 0.1f)
|
||||
);
|
||||
GUI.Label(
|
||||
new Rect(titleRect.x + 10, titleRect.y + 6, titleRect.width - 20, titleRect.height),
|
||||
(mcpClient?.name ?? "Unknown") + " Manual Configuration",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Instructions with improved styling
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
Rect headerRect = EditorGUILayout.GetControlRect(false, 24);
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(headerRect.x, headerRect.y, headerRect.width, headerRect.height),
|
||||
new Color(0.1f, 0.1f, 0.1f, 0.2f)
|
||||
);
|
||||
GUI.Label(
|
||||
new Rect(
|
||||
headerRect.x + 8,
|
||||
headerRect.y + 4,
|
||||
headerRect.width - 16,
|
||||
headerRect.height
|
||||
),
|
||||
"The automatic configuration failed. Please follow these steps:",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
GUIStyle instructionStyle = new(EditorStyles.wordWrappedLabel)
|
||||
{
|
||||
margin = new RectOffset(10, 10, 5, 5),
|
||||
};
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
"1. Open " + (mcpClient?.name ?? "Unknown") + " config file by either:",
|
||||
instructionStyle
|
||||
);
|
||||
if (mcpClient?.mcpType == McpTypes.ClaudeDesktop)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
" a) Going to Settings > Developer > Edit Config",
|
||||
instructionStyle
|
||||
);
|
||||
}
|
||||
else if (mcpClient?.mcpType == McpTypes.Cursor)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
" a) Going to File > Preferences > Cursor Settings > MCP > Add new global MCP server",
|
||||
instructionStyle
|
||||
);
|
||||
}
|
||||
else if (mcpClient?.mcpType == McpTypes.Windsurf)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
" a) Going to File > Preferences > Windsurf Settings > MCP > Manage MCPs -> View raw config",
|
||||
instructionStyle
|
||||
);
|
||||
}
|
||||
else if (mcpClient?.mcpType == McpTypes.Kiro)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
" a) Going to File > Settings > Settings > Search for \"MCP\" > Open Workspace MCP Config",
|
||||
instructionStyle
|
||||
);
|
||||
}
|
||||
else if (mcpClient?.mcpType == McpTypes.Codex)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
" a) Running `codex config edit` in a terminal",
|
||||
instructionStyle
|
||||
);
|
||||
}
|
||||
else if (mcpClient?.mcpType == McpTypes.Trae)
|
||||
{
|
||||
EditorGUILayout.LabelField(
|
||||
" a) Going to Settings > MCP > Add Server > Add Manually",
|
||||
instructionStyle
|
||||
);
|
||||
}
|
||||
EditorGUILayout.LabelField(" OR", instructionStyle);
|
||||
EditorGUILayout.LabelField(
|
||||
" b) Opening the configuration file at:",
|
||||
instructionStyle
|
||||
);
|
||||
|
||||
// Path section with improved styling
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
string displayPath;
|
||||
if (mcpClient != null)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
displayPath = mcpClient.windowsConfigPath;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
displayPath = string.IsNullOrEmpty(mcpClient.macConfigPath)
|
||||
|
||||
? configPath
|
||||
|
||||
: mcpClient.macConfigPath;
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
displayPath = mcpClient.linuxConfigPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayPath = configPath;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
displayPath = configPath;
|
||||
}
|
||||
|
||||
// Prevent text overflow by allowing the text field to wrap
|
||||
GUIStyle pathStyle = new(EditorStyles.textField) { wordWrap = true };
|
||||
|
||||
EditorGUILayout.TextField(
|
||||
displayPath,
|
||||
pathStyle,
|
||||
GUILayout.Height(EditorGUIUtility.singleLineHeight)
|
||||
);
|
||||
|
||||
// Copy button with improved styling
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUIStyle copyButtonStyle = new(GUI.skin.button)
|
||||
{
|
||||
padding = new RectOffset(15, 15, 5, 5),
|
||||
margin = new RectOffset(10, 10, 5, 5),
|
||||
};
|
||||
|
||||
if (
|
||||
GUILayout.Button(
|
||||
"Copy Path",
|
||||
copyButtonStyle,
|
||||
GUILayout.Height(25),
|
||||
GUILayout.Width(100)
|
||||
)
|
||||
)
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = displayPath;
|
||||
pathCopied = true;
|
||||
copyFeedbackTimer = 2f;
|
||||
}
|
||||
|
||||
if (
|
||||
GUILayout.Button(
|
||||
"Open File",
|
||||
copyButtonStyle,
|
||||
GUILayout.Height(25),
|
||||
GUILayout.Width(100)
|
||||
)
|
||||
)
|
||||
{
|
||||
// Open the file using the system's default application
|
||||
System.Diagnostics.Process.Start(
|
||||
new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = displayPath,
|
||||
UseShellExecute = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (pathCopied)
|
||||
{
|
||||
GUIStyle feedbackStyle = new(EditorStyles.label);
|
||||
feedbackStyle.normal.textColor = Color.green;
|
||||
EditorGUILayout.LabelField("Copied!", feedbackStyle, GUILayout.Width(60));
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
string configLabel = mcpClient?.mcpType == McpTypes.Codex
|
||||
? "2. Paste the following TOML configuration:"
|
||||
: "2. Paste the following JSON configuration:";
|
||||
EditorGUILayout.LabelField(configLabel, instructionStyle);
|
||||
|
||||
// JSON section with improved styling
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
// Improved text area for JSON with syntax highlighting colors
|
||||
GUIStyle jsonStyle = new(EditorStyles.textArea)
|
||||
{
|
||||
font = EditorStyles.boldFont,
|
||||
wordWrap = true,
|
||||
};
|
||||
jsonStyle.normal.textColor = new Color(0.3f, 0.6f, 0.9f); // Syntax highlighting blue
|
||||
|
||||
// Draw the JSON in a text area with a taller height for better readability
|
||||
EditorGUILayout.TextArea(configJson, jsonStyle, GUILayout.Height(200));
|
||||
|
||||
// Copy JSON button with improved styling
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (
|
||||
GUILayout.Button(
|
||||
"Copy JSON",
|
||||
copyButtonStyle,
|
||||
GUILayout.Height(25),
|
||||
GUILayout.Width(100)
|
||||
)
|
||||
)
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = configJson;
|
||||
jsonCopied = true;
|
||||
copyFeedbackTimer = 2f;
|
||||
}
|
||||
|
||||
if (jsonCopied)
|
||||
{
|
||||
GUIStyle feedbackStyle = new(EditorStyles.label);
|
||||
feedbackStyle.normal.textColor = Color.green;
|
||||
EditorGUILayout.LabelField("Copied!", feedbackStyle, GUILayout.Width(60));
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField(
|
||||
"3. Save the file and restart " + (mcpClient?.name ?? "Unknown"),
|
||||
instructionStyle
|
||||
);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Close button at the bottom
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("Close", GUILayout.Height(30), GUILayout.Width(100)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
// Handle the feedback message timer
|
||||
if (copyFeedbackTimer > 0)
|
||||
{
|
||||
copyFeedbackTimer -= Time.deltaTime;
|
||||
if (copyFeedbackTimer <= 0)
|
||||
{
|
||||
pathCopied = false;
|
||||
jsonCopied = false;
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 36798bd7b867b8e43ac86885e94f928f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using MCPForUnity.Editor.Models;
|
||||
|
||||
namespace MCPForUnity.Editor.Windows
|
||||
{
|
||||
public class VSCodeManualSetupWindow : ManualConfigEditorWindow
|
||||
{
|
||||
public static void ShowWindow(string configPath, string configJson)
|
||||
{
|
||||
var window = GetWindow<VSCodeManualSetupWindow>("VSCode GitHub Copilot Setup");
|
||||
window.configPath = configPath;
|
||||
window.configJson = configJson;
|
||||
window.minSize = new Vector2(550, 500);
|
||||
|
||||
// Create a McpClient for VSCode
|
||||
window.mcpClient = new McpClient
|
||||
{
|
||||
name = "VSCode GitHub Copilot",
|
||||
mcpType = McpTypes.VSCode
|
||||
};
|
||||
|
||||
window.Show();
|
||||
}
|
||||
|
||||
protected override void OnGUI()
|
||||
{
|
||||
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||||
|
||||
// Header with improved styling
|
||||
EditorGUILayout.Space(10);
|
||||
Rect titleRect = EditorGUILayout.GetControlRect(false, 30);
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(titleRect.x, titleRect.y, titleRect.width, titleRect.height),
|
||||
new Color(0.2f, 0.2f, 0.2f, 0.1f)
|
||||
);
|
||||
GUI.Label(
|
||||
new Rect(titleRect.x + 10, titleRect.y + 6, titleRect.width - 20, titleRect.height),
|
||||
"VSCode GitHub Copilot MCP Setup",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Instructions with improved styling
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
Rect headerRect = EditorGUILayout.GetControlRect(false, 24);
|
||||
EditorGUI.DrawRect(
|
||||
new Rect(headerRect.x, headerRect.y, headerRect.width, headerRect.height),
|
||||
new Color(0.1f, 0.1f, 0.1f, 0.2f)
|
||||
);
|
||||
GUI.Label(
|
||||
new Rect(
|
||||
headerRect.x + 8,
|
||||
headerRect.y + 4,
|
||||
headerRect.width - 16,
|
||||
headerRect.height
|
||||
),
|
||||
"Setting up GitHub Copilot in VSCode with MCP for Unity",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
GUIStyle instructionStyle = new(EditorStyles.wordWrappedLabel)
|
||||
{
|
||||
margin = new RectOffset(10, 10, 5, 5),
|
||||
};
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
"1. Prerequisites",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"• Ensure you have VSCode installed",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"• Ensure you have GitHub Copilot extension installed in VSCode",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"• Ensure you have a valid GitHub Copilot subscription",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
"2. Steps to Configure",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"a) Open or create your VSCode MCP config file (mcp.json) at the path below",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"b) Paste the JSON shown below into mcp.json",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"c) Save the file and restart VSCode",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
"3. VSCode mcp.json location:",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
|
||||
// Path section with improved styling
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
string displayPath;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
displayPath = System.IO.Path.Combine(
|
||||
System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData),
|
||||
"Code",
|
||||
"User",
|
||||
"mcp.json"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
displayPath = System.IO.Path.Combine(
|
||||
System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile),
|
||||
"Library",
|
||||
"Application Support",
|
||||
"Code",
|
||||
"User",
|
||||
"mcp.json"
|
||||
);
|
||||
}
|
||||
|
||||
// Store the path in the base class config path
|
||||
if (string.IsNullOrEmpty(configPath))
|
||||
{
|
||||
configPath = displayPath;
|
||||
}
|
||||
|
||||
// Prevent text overflow by allowing the text field to wrap
|
||||
GUIStyle pathStyle = new(EditorStyles.textField) { wordWrap = true };
|
||||
|
||||
EditorGUILayout.TextField(
|
||||
displayPath,
|
||||
pathStyle,
|
||||
GUILayout.Height(EditorGUIUtility.singleLineHeight)
|
||||
);
|
||||
|
||||
// Copy button with improved styling
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUIStyle copyButtonStyle = new(GUI.skin.button)
|
||||
{
|
||||
padding = new RectOffset(15, 15, 5, 5),
|
||||
margin = new RectOffset(10, 10, 5, 5),
|
||||
};
|
||||
|
||||
if (
|
||||
GUILayout.Button(
|
||||
"Copy Path",
|
||||
copyButtonStyle,
|
||||
GUILayout.Height(25),
|
||||
GUILayout.Width(100)
|
||||
)
|
||||
)
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = displayPath;
|
||||
pathCopied = true;
|
||||
copyFeedbackTimer = 2f;
|
||||
}
|
||||
|
||||
if (
|
||||
GUILayout.Button(
|
||||
"Open File",
|
||||
copyButtonStyle,
|
||||
GUILayout.Height(25),
|
||||
GUILayout.Width(100)
|
||||
)
|
||||
)
|
||||
{
|
||||
// Open the file using the system's default application
|
||||
System.Diagnostics.Process.Start(
|
||||
new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = displayPath,
|
||||
UseShellExecute = true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (pathCopied)
|
||||
{
|
||||
GUIStyle feedbackStyle = new(EditorStyles.label);
|
||||
feedbackStyle.normal.textColor = Color.green;
|
||||
EditorGUILayout.LabelField("Copied!", feedbackStyle, GUILayout.Width(60));
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
"4. Add this configuration to your mcp.json:",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
|
||||
// JSON section with improved styling
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
// Improved text area for JSON with syntax highlighting colors
|
||||
GUIStyle jsonStyle = new(EditorStyles.textArea)
|
||||
{
|
||||
font = EditorStyles.boldFont,
|
||||
wordWrap = true,
|
||||
};
|
||||
jsonStyle.normal.textColor = new Color(0.3f, 0.6f, 0.9f); // Syntax highlighting blue
|
||||
|
||||
// Draw the JSON in a text area with a taller height for better readability
|
||||
EditorGUILayout.TextArea(configJson, jsonStyle, GUILayout.Height(200));
|
||||
|
||||
// Copy JSON button with improved styling
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (
|
||||
GUILayout.Button(
|
||||
"Copy JSON",
|
||||
copyButtonStyle,
|
||||
GUILayout.Height(25),
|
||||
GUILayout.Width(100)
|
||||
)
|
||||
)
|
||||
{
|
||||
EditorGUIUtility.systemCopyBuffer = configJson;
|
||||
jsonCopied = true;
|
||||
copyFeedbackTimer = 2f;
|
||||
}
|
||||
|
||||
if (jsonCopied)
|
||||
{
|
||||
GUIStyle feedbackStyle = new(EditorStyles.label);
|
||||
feedbackStyle.normal.textColor = Color.green;
|
||||
EditorGUILayout.LabelField("Copied!", feedbackStyle, GUILayout.Width(60));
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField(
|
||||
"5. After configuration:",
|
||||
EditorStyles.boldLabel
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"• Restart VSCode",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"• GitHub Copilot will now be able to interact with your Unity project through the MCP protocol",
|
||||
instructionStyle
|
||||
);
|
||||
EditorGUILayout.LabelField(
|
||||
"• Remember to have the MCP for Unity Bridge running in Unity Editor",
|
||||
instructionStyle
|
||||
);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Close button at the bottom
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("Close", GUILayout.Height(30), GUILayout.Width(100)))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Call the base implementation which handles the copy feedback timer
|
||||
base.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 377fe73d52cf0435fabead5f50a0d204
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -3,7 +3,7 @@ name = "MCPForUnityServer"
|
|||
version = "6.2.5"
|
||||
description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"httpx>=0.27.2",
|
||||
"fastmcp>=2.12.5",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
|
|
@ -16,7 +16,6 @@ name = "anyio"
|
|||
version = "4.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
|
|
@ -36,12 +35,15 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "backports-asyncio-runner"
|
||||
version = "1.2.0"
|
||||
name = "authlib"
|
||||
version = "1.6.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/3f/1d3bbd0bf23bdd99276d4def22f29c27a914067b4cf66f753ff9b8bbd0f3/authlib-1.6.5.tar.gz", hash = "sha256:6aaf9c79b7cc96c900f0b284061691c5d4e61221640a948fe690b556a6d6d10b", size = 164553, upload-time = "2025-10-02T13:36:09.489Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/aa/5082412d1ee302e9e7d80b6949bc4d2a8fa1149aaab610c5fc24709605d6/authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a", size = 243608, upload-time = "2025-10-02T13:36:07.637Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -53,6 +55,149 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
|
|
@ -74,6 +219,123 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "46.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/8a/e60e46adab4362a682cf142c7dcb5bf79b782ab2199b0dcb81f55970807f/cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea", size = 3698132, upload-time = "2025-10-15T23:18:17.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/38/f59940ec4ee91e93d3311f7532671a5cef5570eb04a144bf203b58552d11/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b", size = 4243992, upload-time = "2025-10-15T23:18:18.695Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/0c/35b3d92ddebfdfda76bb485738306545817253d0a3ded0bfe80ef8e67aa5/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb", size = 4409944, upload-time = "2025-10-15T23:18:20.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/55/181022996c4063fc0e7666a47049a1ca705abb9c8a13830f074edb347495/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717", size = 4242957, upload-time = "2025-10-15T23:18:22.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c3/e90f4a4feae6410f914f8ebac129b9ae7a8c92eb60a638012dde42030a9d/cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c", size = 3438528, upload-time = "2025-10-15T23:18:26.227Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cyclopts"
|
||||
version = "4.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "attrs" },
|
||||
{ name = "docstring-parser" },
|
||||
{ name = "rich" },
|
||||
{ name = "rich-rst" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9a/d1/2f2b99ec5ea54ac18baadfc4a011e2a1743c1eaae1e39838ca520dcf4811/cyclopts-4.0.0.tar.gz", hash = "sha256:0dae712085e91d32cc099ea3d78f305b0100a3998b1dec693be9feb0b1be101f", size = 143546, upload-time = "2025-10-20T18:33:01.456Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/44/0e/0a22e076944600aeb06f40b7e03bbd762a42d56d43a2f5f4ab954aed9005/cyclopts-4.0.0-py3-none-any.whl", hash = "sha256:e64801a2c86b681f08323fd50110444ee961236a0bae402a66d2cc3feda33da7", size = 178837, upload-time = "2025-10-20T18:33:00.191Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docstring-parser"
|
||||
version = "0.17.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.22.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4a/c0/89fe6215b443b919cb98a5002e107cb5026854ed1ccb6b5833e0768419d1/docutils-0.22.2.tar.gz", hash = "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d", size = 2289092, upload-time = "2025-09-20T17:55:47.994Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/66/dd/f95350e853a4468ec37478414fc04ae2d61dad7a947b3015c3dcc51a09b9/docutils-0.22.2-py3-none-any.whl", hash = "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8", size = 632667, upload-time = "2025-09-20T17:55:43.052Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email-validator"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "dnspython" },
|
||||
{ name = "idna" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.0"
|
||||
|
|
@ -86,6 +348,28 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastmcp"
|
||||
version = "2.12.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "authlib" },
|
||||
{ name = "cyclopts" },
|
||||
{ name = "exceptiongroup" },
|
||||
{ name = "httpx" },
|
||||
{ name = "mcp" },
|
||||
{ name = "openapi-core" },
|
||||
{ name = "openapi-pydantic" },
|
||||
{ name = "pydantic", extra = ["email"] },
|
||||
{ name = "pyperclip" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/00/a6/e3b46cd3e228635e0064c2648788b6f66a53bf0d0ddbf5fb44cca951f908/fastmcp-2.12.5.tar.gz", hash = "sha256:2dfd02e255705a4afe43d26caddbc864563036e233dbc6870f389ee523b39a6a", size = 7190263, upload-time = "2025-10-17T13:24:58.896Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/c1/9fb98c9649e15ea8cc691b4b09558b61dafb3dc0345f7322f8c4a8991ade/fastmcp-2.12.5-py3-none-any.whl", hash = "sha256:b1e542f9b83dbae7cecfdc9c73b062f77074785abda9f2306799116121344133", size = 329099, upload-time = "2025-10-17T13:24:57.518Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
|
|
@ -150,6 +434,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "isodate"
|
||||
version = "0.7.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.25.1"
|
||||
|
|
@ -165,6 +458,21 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema-path"
|
||||
version = "0.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pathable" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "referencing" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159, upload-time = "2025-01-24T14:33:16.547Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810, upload-time = "2025-01-24T14:33:14.652Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema-specifications"
|
||||
version = "2025.9.1"
|
||||
|
|
@ -177,6 +485,45 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy-object-proxy"
|
||||
version = "1.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681, upload-time = "2025-08-22T13:50:06.783Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/b3/4684b1e128a87821e485f5a901b179790e6b5bc02f89b7ee19c23be36ef3/lazy_object_proxy-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1cf69cd1a6c7fe2dbcc3edaa017cf010f4192e53796538cc7d5e1fedbfa4bcff", size = 26656, upload-time = "2025-08-22T13:42:30.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/03/1bdc21d9a6df9ff72d70b2ff17d8609321bea4b0d3cffd2cea92fb2ef738/lazy_object_proxy-1.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:efff4375a8c52f55a145dc8487a2108c2140f0bec4151ab4e1843e52eb9987ad", size = 68832, upload-time = "2025-08-22T13:42:31.675Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/4b/5788e5e8bd01d19af71e50077ab020bc5cce67e935066cd65e1215a09ff9/lazy_object_proxy-1.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1192e8c2f1031a6ff453ee40213afa01ba765b3dc861302cd91dbdb2e2660b00", size = 69148, upload-time = "2025-08-22T13:42:32.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/0e/090bf070f7a0de44c61659cb7f74c2fe02309a77ca8c4b43adfe0b695f66/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3605b632e82a1cbc32a1e5034278a64db555b3496e0795723ee697006b980508", size = 67800, upload-time = "2025-08-22T13:42:34.054Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/d2/b320325adbb2d119156f7c506a5fbfa37fcab15c26d13cf789a90a6de04e/lazy_object_proxy-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a61095f5d9d1a743e1e20ec6d6db6c2ca511961777257ebd9b288951b23b44fa", size = 68085, upload-time = "2025-08-22T13:42:35.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/48/4b718c937004bf71cd82af3713874656bcb8d0cc78600bf33bb9619adc6c/lazy_object_proxy-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:997b1d6e10ecc6fb6fe0f2c959791ae59599f41da61d652f6c903d1ee58b7370", size = 26535, upload-time = "2025-08-22T13:42:36.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/1b/b5f5bd6bda26f1e15cd3232b223892e4498e34ec70a7f4f11c401ac969f1/lazy_object_proxy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede", size = 26746, upload-time = "2025-08-22T13:42:37.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/64/314889b618075c2bfc19293ffa9153ce880ac6153aacfd0a52fcabf21a66/lazy_object_proxy-1.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9", size = 71457, upload-time = "2025-08-22T13:42:38.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/53/857fc2827fc1e13fbdfc0ba2629a7d2579645a06192d5461809540b78913/lazy_object_proxy-1.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0", size = 71036, upload-time = "2025-08-22T13:42:40.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/24/e581ffed864cd33c1b445b5763d617448ebb880f48675fc9de0471a95cbc/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308", size = 69329, upload-time = "2025-08-22T13:42:41.311Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/be/15f8f5a0b0b2e668e756a152257d26370132c97f2f1943329b08f057eff0/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23", size = 70690, upload-time = "2025-08-22T13:42:42.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/aa/f02be9bbfb270e13ee608c2b28b8771f20a5f64356c6d9317b20043c6129/lazy_object_proxy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073", size = 26563, upload-time = "2025-08-22T13:42:43.685Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745, upload-time = "2025-08-22T13:42:44.982Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537, upload-time = "2025-08-22T13:42:46.141Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141, upload-time = "2025-08-22T13:42:47.375Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449, upload-time = "2025-08-22T13:42:48.49Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744, upload-time = "2025-08-22T13:42:49.608Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568, upload-time = "2025-08-22T13:42:57.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391, upload-time = "2025-08-22T13:42:50.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552, upload-time = "2025-08-22T13:42:51.731Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857, upload-time = "2025-08-22T13:42:53.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833, upload-time = "2025-08-22T13:42:54.391Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516, upload-time = "2025-08-22T13:42:55.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656, upload-time = "2025-08-22T13:42:56.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/3a/277857b51ae419a1574557c0b12e0d06bf327b758ba94cafc664cb1e2f66/lazy_object_proxy-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3", size = 26582, upload-time = "2025-08-22T13:49:49.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/b6/c5e0fa43535bb9c87880e0ba037cdb1c50e01850b0831e80eb4f4762f270/lazy_object_proxy-1.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a", size = 71059, upload-time = "2025-08-22T13:49:50.488Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/8a/7dcad19c685963c652624702f1a968ff10220b16bfcc442257038216bf55/lazy_object_proxy-1.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a", size = 71034, upload-time = "2025-08-22T13:49:54.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/ac/34cbfb433a10e28c7fd830f91c5a348462ba748413cbb950c7f259e67aa7/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95", size = 69529, upload-time = "2025-08-22T13:49:55.29Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/6a/11ad7e349307c3ca4c0175db7a77d60ce42a41c60bcb11800aabd6a8acb8/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5", size = 70391, upload-time = "2025-08-22T13:49:56.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/97/9b410ed8fbc6e79c1ee8b13f8777a80137d4bc189caf2c6202358e66192c/lazy_object_proxy-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f", size = 26988, upload-time = "2025-08-22T13:49:57.302Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/a0/b91504515c1f9a299fc157967ffbd2f0321bce0516a3d5b89f6f4cad0355/lazy_object_proxy-1.12.0-pp39.pp310.pp311.graalpy311-none-any.whl", hash = "sha256:c3b2e0af1f7f77c4263759c4824316ce458fabe0fceadcd24ef8ca08b2d1e402", size = 15072, upload-time = "2025-08-22T13:50:05.498Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
|
|
@ -189,9 +536,83 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcp"
|
||||
version = "1.18.0"
|
||||
version = "1.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
|
|
@ -206,24 +627,19 @@ dependencies = [
|
|||
{ name = "starlette" },
|
||||
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/e0/fe34ce16ea2bacce489ab859abd1b47ae28b438c3ef60b9c5eee6c02592f/mcp-1.18.0.tar.gz", hash = "sha256:aa278c44b1efc0a297f53b68df865b988e52dd08182d702019edcf33a8e109f6", size = 482926, upload-time = "2025-10-16T19:19:55.125Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/a1/b1f328da3b153683d2ec34f849b4b6eac2790fb240e3aef06ff2fab3df9d/mcp-1.16.0.tar.gz", hash = "sha256:39b8ca25460c578ee2cdad33feeea122694cfdf73eef58bee76c42f6ef0589df", size = 472918, upload-time = "2025-10-02T16:58:20.631Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/44/f5970e3e899803823826283a70b6003afd46f28e082544407e24575eccd3/mcp-1.18.0-py3-none-any.whl", hash = "sha256:42f10c270de18e7892fdf9da259029120b1ea23964ff688248c69db9d72b1d0a", size = 168762, upload-time = "2025-10-16T19:19:53.2Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
cli = [
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "typer" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/0e/7cebc88e17daf94ebe28c95633af595ccb2864dc2ee7abd75542d98495cc/mcp-1.16.0-py3-none-any.whl", hash = "sha256:ec917be9a5d31b09ba331e1768aa576e0af45470d657a0319996a20a57d7d633", size = 167266, upload-time = "2025-10-02T16:58:19.039Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcpforunityserver"
|
||||
version = "6.2.1"
|
||||
version = "6.2.5"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
{ name = "httpx" },
|
||||
{ name = "mcp", extra = ["cli"] },
|
||||
{ name = "mcp" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "tomli" },
|
||||
]
|
||||
|
|
@ -236,8 +652,9 @@ dev = [
|
|||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "fastmcp", specifier = ">=2.12.5" },
|
||||
{ name = "httpx", specifier = ">=0.27.2" },
|
||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.17.0" },
|
||||
{ name = "mcp", specifier = ">=1.16.0" },
|
||||
{ name = "pydantic", specifier = ">=2.12.0" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
|
||||
|
|
@ -254,6 +671,76 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "10.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openapi-core"
|
||||
version = "0.19.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "isodate" },
|
||||
{ name = "jsonschema" },
|
||||
{ name = "jsonschema-path" },
|
||||
{ name = "more-itertools" },
|
||||
{ name = "openapi-schema-validator" },
|
||||
{ name = "openapi-spec-validator" },
|
||||
{ name = "parse" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "werkzeug" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/35/1acaa5f2fcc6e54eded34a2ec74b479439c4e469fc4e8d0e803fda0234db/openapi_core-0.19.5.tar.gz", hash = "sha256:421e753da56c391704454e66afe4803a290108590ac8fa6f4a4487f4ec11f2d3", size = 103264, upload-time = "2025-03-20T20:17:28.193Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/6f/83ead0e2e30a90445ee4fc0135f43741aebc30cca5b43f20968b603e30b6/openapi_core-0.19.5-py3-none-any.whl", hash = "sha256:ef7210e83a59394f46ce282639d8d26ad6fc8094aa904c9c16eb1bac8908911f", size = 106595, upload-time = "2025-03-20T20:17:26.77Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openapi-pydantic"
|
||||
version = "0.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openapi-schema-validator"
|
||||
version = "0.6.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jsonschema" },
|
||||
{ name = "jsonschema-specifications" },
|
||||
{ name = "rfc3339-validator" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/5507ad3325169347cd8ced61c232ff3df70e2b250c49f0fe140edb4973c6/openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee", size = 11550, upload-time = "2025-01-10T18:08:22.268Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/21/c6/ad0fba32775ae749016829dace42ed80f4407b171da41313d1a3a5f102e4/openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3", size = 8755, upload-time = "2025-01-10T18:08:19.758Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openapi-spec-validator"
|
||||
version = "0.7.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jsonschema" },
|
||||
{ name = "jsonschema-path" },
|
||||
{ name = "lazy-object-proxy" },
|
||||
{ name = "openapi-schema-validator" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/af/fe2d7618d6eae6fb3a82766a44ed87cd8d6d82b4564ed1c7cfb0f6378e91/openapi_spec_validator-0.7.2.tar.gz", hash = "sha256:cc029309b5c5dbc7859df0372d55e9d1ff43e96d678b9ba087f7c56fc586f734", size = 36855, upload-time = "2025-06-07T14:48:56.299Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/dd/b3fd642260cb17532f66cc1e8250f3507d1e580483e209dc1e9d13bd980d/openapi_spec_validator-0.7.2-py3-none-any.whl", hash = "sha256:4bbdc0894ec85f1d1bea1d6d9c8b2c3c8d7ccaa13577ef40da9c006c9fd0eb60", size = 39713, upload-time = "2025-06-07T14:48:54.077Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
|
|
@ -263,6 +750,24 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse"
|
||||
version = "1.20.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4f/78/d9b09ba24bb36ef8b83b71be547e118d46214735b6dfb39e4bfde0e9b9dd/parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce", size = 29391, upload-time = "2024-06-11T04:41:57.34Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126, upload-time = "2024-06-11T04:41:55.057Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathable"
|
||||
version = "0.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124, upload-time = "2025-01-10T18:43:13.247Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
|
|
@ -272,6 +777,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.23"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.0"
|
||||
|
|
@ -287,6 +801,11 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/f4/9d/d5c855424e2e5b6b626fbc6ec514d8e655a600377ce283008b115abb7445/pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f", size = 459730, upload-time = "2025-10-07T15:58:01.576Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
email = [
|
||||
{ name = "email-validator" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.41.1"
|
||||
|
|
@ -296,19 +815,6 @@ dependencies = [
|
|||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/14/12b4a0d2b0b10d8e1d9a24ad94e7bbb43335eaf29c0c4e57860e8a30734a/pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f", size = 454870, upload-time = "2025-10-07T10:50:45.974Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/2c/a5c4640dc7132540109f67fe83b566fbc7512ccf2a068cfa22a243df70c7/pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61", size = 2113814, upload-time = "2025-10-06T21:09:50.892Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/e7/a8694c3454a57842095d69c7a4ab3cf81c3c7b590f052738eabfdfc2e234/pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936", size = 1916660, upload-time = "2025-10-06T21:09:52.783Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/58/29f12e65b19c1877a0269eb4f23c5d2267eded6120a7d6762501ab843dc9/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0", size = 1975071, upload-time = "2025-10-06T21:09:54.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/26/4e677f2b7ec3fbdd10be6b586a82a814c8ebe3e474024c8df2d4260e564e/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e", size = 2067271, upload-time = "2025-10-06T21:09:55.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/50/50614bd906089904d7ca1be3b9ecf08c00a327143d48f1decfdc21b3c302/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538", size = 2253207, upload-time = "2025-10-06T21:09:56.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/58/b1e640b4ca559273cca7c28e0fe8891d5d8e9a600f5ab4882670ec107549/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350", size = 2375052, upload-time = "2025-10-06T21:09:57.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/25/cd47df3bfb24350e03835f0950288d1054f1cc9a8023401dabe6d4ff2834/pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917", size = 2076834, upload-time = "2025-10-06T21:09:59.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/b4/71b2c77e5df527fbbc1a03e72c3fd96c44cd10d4241a81befef8c12b9fc4/pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5", size = 2195374, upload-time = "2025-10-06T21:10:01.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/08/4b8a50733005865efde284fec45da75fe16a258f706e16323c5ace4004eb/pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897", size = 2156060, upload-time = "2025-10-06T21:10:02.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/c3/1037cb603ef2130c210150a51b1710d86825b5c28df54a55750099f91196/pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb", size = 2331640, upload-time = "2025-10-06T21:10:04.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/4c/52d111869610e6b1a46e1f1035abcdc94d0655587e39104433a290e9f377/pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13", size = 2329844, upload-time = "2025-10-06T21:10:05.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/5d/4b435f0b52ab543967761aca66b84ad3f0026e491e57de47693d15d0a8db/pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb", size = 1991289, upload-time = "2025-10-06T21:10:07.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/52/31b4deafc1d3cb96d0e7c0af70f0dc05454982d135d07f5117e6336153e8/pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e", size = 2027747, upload-time = "2025-10-06T21:10:08.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/a9/ec440f02e57beabdfd804725ef1e38ac1ba00c49854d298447562e119513/pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1", size = 2111456, upload-time = "2025-10-06T21:10:09.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/f9/6bc15bacfd8dcfc073a1820a564516d9c12a435a9a332d4cbbfd48828ddd/pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176", size = 1915012, upload-time = "2025-10-06T21:10:11.599Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/8a/d9edcdcdfe80bade17bed424284427c08bea892aaec11438fa52eaeaf79c/pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0", size = 1973762, upload-time = "2025-10-06T21:10:13.154Z" },
|
||||
|
|
@ -379,14 +885,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/5a/bd/389504c9e0600ef4502cd5238396b527afe6ef8981a6a15cd1814fc7b434/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb", size = 1927994, upload-time = "2025-10-07T10:50:04.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/9c/5111c6b128861cb792a4c082677e90dac4f2e090bb2e2fe06aa5b2d39027/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca", size = 1959394, upload-time = "2025-10-07T10:50:06.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/3f/cfec8b9a0c48ce5d64409ec5e1903cb0b7363da38f14b41de2fcb3712700/pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28", size = 2147365, upload-time = "2025-10-07T10:50:07.978Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/31/f403d7ca8352e3e4df352ccacd200f5f7f7fe81cef8e458515f015091625/pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0", size = 2114268, upload-time = "2025-10-07T10:50:10.257Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/b5/334473b6d2810df84db67f03d4f666acacfc538512c2d2a254074fee0889/pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08", size = 1935786, upload-time = "2025-10-07T10:50:12.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/5e/45513e4dc621f47397cfa5fef12ba8fa5e8b1c4c07f2ff2a5fef8ff81b25/pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52", size = 1971995, upload-time = "2025-10-07T10:50:14.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/e3/f1797c168e5f52b973bed1c585e99827a22d5e579d1ed57d51bc15b14633/pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01", size = 2191264, upload-time = "2025-10-07T10:50:15.788Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/e1/24ef4c3b4ab91c21c3a09a966c7d2cffe101058a7bfe5cc8b2c7c7d574e2/pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1", size = 2152430, upload-time = "2025-10-07T10:50:18.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/74/70c1e225d67f7ef3fdba02c506d9011efaf734020914920b2aa3d1a45e61/pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1", size = 2324691, upload-time = "2025-10-07T10:50:19.801Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/bf/dd4d21037c8bef0d8cce90a86a3f2dcb011c30086db2a10113c3eea23eba/pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65", size = 2324493, upload-time = "2025-10-07T10:50:21.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/78/3093b334e9c9796c8236a4701cd2ddef1c56fb0928fe282a10c797644380/pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301", size = 2146156, upload-time = "2025-10-07T10:50:23.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/6c/fa3e45c2b054a1e627a89a364917f12cbe3abc3e91b9004edaae16e7b3c5/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1", size = 2112094, upload-time = "2025-10-07T10:50:25.513Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/17/7eebc38b4658cc8e6902d0befc26388e4c2a5f2e179c561eeb43e1922c7b/pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696", size = 1935300, upload-time = "2025-10-07T10:50:27.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/00/9fe640194a1717a464ab861d43595c268830f98cb1e2705aa134b3544b70/pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222", size = 1970417, upload-time = "2025-10-07T10:50:29.573Z" },
|
||||
|
|
@ -419,18 +917,25 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyperclip"
|
||||
version = "1.11.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" }
|
||||
wheels = [
|
||||
|
|
@ -442,7 +947,6 @@ name = "pytest-asyncio"
|
|||
version = "1.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
|
||||
{ name = "pytest" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
|
|
@ -474,9 +978,6 @@ name = "pywin32"
|
|||
version = "311"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" },
|
||||
|
|
@ -491,6 +992,61 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.36.2"
|
||||
|
|
@ -505,6 +1061,33 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc3339-validator"
|
||||
version = "0.1.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.4"
|
||||
|
|
@ -512,33 +1095,31 @@ source = { registry = "https://pypi.org/simple" }
|
|||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich-rst"
|
||||
version = "1.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "docutils" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpds-py"
|
||||
version = "0.27.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" },
|
||||
|
|
@ -627,19 +1208,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" },
|
||||
|
|
@ -655,12 +1223,12 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
version = "1.5.4"
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -746,21 +1314,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
version = "0.20.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "rich" },
|
||||
{ name = "shellingham" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492, upload-time = "2025-10-20T17:03:49.445Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028, upload-time = "2025-10-20T17:03:47.617Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
|
|
@ -782,6 +1335,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.34.0"
|
||||
|
|
@ -789,9 +1351,20 @@ source = { registry = "https://pypi.org/simple" }
|
|||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "h11" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568, upload-time = "2024-12-15T13:33:30.42Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315, upload-time = "2024-12-15T13:33:27.467Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "3.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markupsafe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/af/d4502dc713b4ccea7175d764718d5183caf8d0867a4f0190d5d4a45cea49/werkzeug-3.1.1.tar.gz", hash = "sha256:8cd39dfbdfc1e051965f156163e2974e52c210f130810e9ad36858f0fd3edad4", size = 806453, upload-time = "2024-11-01T16:40:45.462Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/ea/c67e1dee1ba208ed22c06d1d547ae5e293374bfc43e0eb0ef5e262b68561/werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5", size = 224371, upload-time = "2024-11-01T16:40:43.994Z" },
|
||||
]
|
||||
|
|
|
|||
56
README.md
|
|
@ -1,4 +1,4 @@
|
|||
<img width="676" height="380" alt="MCP for Unity" src="logo.png" />
|
||||
<img width="676" height="380" alt="MCP for Unity" src="docs/images/logo.png" />
|
||||
|
||||
| [English](README.md) | [简体中文](README-zh.md) |
|
||||
|----------------------|---------------------------------|
|
||||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
MCP for Unity acts as a bridge, allowing AI assistants (like Claude, Cursor) to interact directly with your Unity Editor via a local **MCP (Model Context Protocol) Client**. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
### 💬 Join Our [Discord](https://discord.gg/y4p8KfzrN4)
|
||||
|
|
@ -34,7 +36,7 @@ MCP for Unity acts as a bridge, allowing AI assistants (like Claude, Cursor) to
|
|||
* **🧩 Extensible:** Designed to work with various MCP Clients.
|
||||
|
||||
<details open>
|
||||
<summary><strong> Available Tools </strong></summary>
|
||||
<summary><strong>Tools</strong></summary>
|
||||
|
||||
Your LLM can use functions like:
|
||||
|
||||
|
|
@ -49,8 +51,18 @@ MCP for Unity acts as a bridge, allowing AI assistants (like Claude, Cursor) to
|
|||
* `apply_text_edits`: Precise text edits with precondition hashes and atomic multi-edit batches.
|
||||
* `script_apply_edits`: Structured C# method/class edits (insert/replace/delete) with safer boundaries.
|
||||
* `validate_script`: Fast validation (basic/standard) to catch syntax/structure issues before/after writes.
|
||||
* `run_test`: Runs a tests in the Unity Editor.
|
||||
</details>
|
||||
|
||||
|
||||
<details open>
|
||||
<summary><strong>Resources</strong></summary>
|
||||
|
||||
Your LLM can retrieve the following resources:
|
||||
|
||||
* `menu_items`: Retrieves all available menu items in the Unity Editor.
|
||||
* `tests`: Retrieves all available tests in the Unity Editor. Can select tests of a specific type (e.g., "EditMode", "PlayMode").
|
||||
</details>
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
|
@ -68,7 +80,7 @@ MCP for Unity connects your tools using two components:
|
|||
|
||||
### Prerequisites
|
||||
|
||||
* **Python:** Version 3.12 or newer. [Download Python](https://www.python.org/downloads/)
|
||||
* **Python:** Version 3.11 or newer. [Download Python](https://www.python.org/downloads/)
|
||||
* **Unity Hub & Editor:** Version 2021.3 LTS or newer. [Download Unity](https://unity.com/download)
|
||||
* **uv (Python toolchain manager):**
|
||||
```bash
|
||||
|
|
@ -118,7 +130,6 @@ MCP for Unity connects your tools using two components:
|
|||
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity
|
||||
```
|
||||
5. Click `Add`.
|
||||
6. The MCP server is installed automatically by the package on first run or via Auto-Setup. If that fails, use Manual Configuration (below).
|
||||
|
||||
#### To install via OpenUPM
|
||||
|
||||
|
|
@ -131,8 +142,6 @@ MCP for Unity connects your tools using two components:
|
|||
### 🛠️ Step 2: Configure Your MCP Client
|
||||
Connect your MCP Client (Claude, Cursor, etc.) to the Python server set up in Step 1 (auto) or via Manual Configuration (below).
|
||||
|
||||
<img width="648" height="599" alt="MCPForUnity-Readme-Image" src="https://github.com/user-attachments/assets/b4a725da-5c43-4bd6-80d6-ee2e3cca9596" />
|
||||
|
||||
**Option A: Auto-Setup (Recommended for Claude/Cursor/VSC Copilot)**
|
||||
|
||||
1. In Unity, go to `Window > MCP for Unity`.
|
||||
|
|
@ -273,30 +282,23 @@ On Windows, set `command` to the absolute shim, e.g. `C:\\Users\\YOU\\AppData\\L
|
|||
|
||||
## Development & Contributing 🛠️
|
||||
|
||||
### Development Setup and Guidelines
|
||||
|
||||
See [README-DEV.md](docs/README-DEV.md) for complete development setup and workflow documentation.
|
||||
|
||||
### Adding Custom Tools
|
||||
|
||||
MCP for Unity uses a Python MCP Server tied with Unity's C# scripts for tools. If you'd like to extend the functionality with your own tools, learn how to do so in **[CUSTOM_TOOLS.md](docs/CUSTOM_TOOLS.md)**.
|
||||
|
||||
### Contributing to the Project
|
||||
|
||||
If you're contributing to MCP for Unity or want to test core changes, we have development tools to streamline your workflow:
|
||||
|
||||
- **Development Deployment Scripts**: Quickly deploy and test your changes to MCP for Unity Bridge and Python Server
|
||||
- **Automatic Backup System**: Safe testing with easy rollback capabilities
|
||||
- **Hot Reload Workflow**: Fast iteration cycle for core development
|
||||
|
||||
📖 **See [README-DEV.md](docs/README-DEV.md)** for complete development setup and workflow documentation.
|
||||
|
||||
### Contributing 🤝
|
||||
|
||||
Help make MCP for Unity better!
|
||||
### How to Contribute
|
||||
|
||||
1. **Fork** the main repository.
|
||||
2. **Create a branch** (`feature/your-idea` or `bugfix/your-fix`).
|
||||
3. **Make changes.**
|
||||
4. **Commit** (feat: Add cool new feature).
|
||||
5. **Push** your branch.
|
||||
6. **Open a Pull Request** against the main branch.
|
||||
2. **Create an issue** to discuss your idea or bug.
|
||||
3. **Create a branch** (`feature/your-idea` or `bugfix/your-fix`).
|
||||
4. **Make changes.**
|
||||
5. **Commit** (feat: Add cool new feature).
|
||||
6. **Push** your branch.
|
||||
7. **Open a Pull Request** against the main branch, referencing the issue you created earlier.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -332,8 +334,8 @@ Your privacy matters to us. All telemetry is optional and designed to respect yo
|
|||
cd /path/to/your/UnityMCP/UnityMcpServer/src
|
||||
uv run server.py
|
||||
```
|
||||
- **Auto-Configure Failed:**
|
||||
- Use the Manual Configuration steps. Auto-configure might lack permissions to write to the MCP client's config file.
|
||||
- **Configuration Failed:**
|
||||
- Use the Manual Configuration steps. The plugin may lack permissions to write to the MCP client's config file.
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -359,6 +361,8 @@ Coplay offers 2 AI tools for Unity
|
|||
|
||||
(These tools have different tech stacks. See this blog post [comparing Coplay to MCP for Unity](https://www.coplay.dev/blog/comparing-coplay-and-unity-mcp).)
|
||||
|
||||
<img alt="Coplay" src="docs/images/coplay-logo.png" />
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is a free and open-source tool for the Unity Editor, and is not affiliated with Unity Technologies.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,38 @@
|
|||
using NUnit.Framework;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.External.Tommy;
|
||||
using MCPForUnity.Editor.Services;
|
||||
using System.IO;
|
||||
|
||||
namespace MCPForUnityTests.Editor.Helpers
|
||||
{
|
||||
public class CodexConfigHelperTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Mock platform service for testing
|
||||
/// </summary>
|
||||
private class MockPlatformService : IPlatformService
|
||||
{
|
||||
private readonly bool _isWindows;
|
||||
private readonly string _systemRoot;
|
||||
|
||||
public MockPlatformService(bool isWindows, string systemRoot = "C:\\Windows")
|
||||
{
|
||||
_isWindows = isWindows;
|
||||
_systemRoot = systemRoot;
|
||||
}
|
||||
|
||||
public bool IsWindows() => _isWindows;
|
||||
public string GetSystemRoot() => _isWindows ? _systemRoot : null;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Reset service locator after each test
|
||||
MCPServiceLocator.Reset();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryParseCodexServer_SingleLineArgs_ParsesSuccessfully()
|
||||
{
|
||||
|
|
@ -99,5 +127,197 @@ namespace MCPForUnityTests.Editor.Helpers
|
|||
Assert.AreEqual("uv", command);
|
||||
CollectionAssert.AreEqual(new[] { "run", "--directory", "/Users/O'Connor/codex", "server.py" }, args);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildCodexServerBlock_OnWindows_IncludesSystemRootEnv()
|
||||
{
|
||||
// This test verifies the fix for https://github.com/CoplayDev/unity-mcp/issues/315
|
||||
// On Windows, Codex requires SystemRoot environment variable to be set
|
||||
|
||||
// Mock Windows platform
|
||||
MCPServiceLocator.Register<IPlatformService>(new MockPlatformService(isWindows: true, systemRoot: "C:\\Windows"));
|
||||
|
||||
string uvPath = "C:\\path\\to\\uv.exe";
|
||||
string serverSrc = "C:\\path\\to\\server";
|
||||
|
||||
string result = CodexConfigHelper.BuildCodexServerBlock(uvPath, serverSrc);
|
||||
|
||||
Assert.IsNotNull(result, "BuildCodexServerBlock should return a valid TOML string");
|
||||
|
||||
// Parse the generated TOML to validate structure
|
||||
TomlTable parsed;
|
||||
using (var reader = new StringReader(result))
|
||||
{
|
||||
parsed = TOML.Parse(reader);
|
||||
}
|
||||
|
||||
// Verify basic structure
|
||||
Assert.IsTrue(parsed.TryGetNode("mcp_servers", out var mcpServersNode), "TOML should contain mcp_servers");
|
||||
Assert.IsInstanceOf<TomlTable>(mcpServersNode, "mcp_servers should be a table");
|
||||
|
||||
var mcpServers = mcpServersNode as TomlTable;
|
||||
Assert.IsTrue(mcpServers.TryGetNode("unityMCP", out var unityMcpNode), "mcp_servers should contain unityMCP");
|
||||
Assert.IsInstanceOf<TomlTable>(unityMcpNode, "unityMCP should be a table");
|
||||
|
||||
var unityMcp = unityMcpNode as TomlTable;
|
||||
Assert.IsTrue(unityMcp.TryGetNode("command", out _), "unityMCP should contain command");
|
||||
Assert.IsTrue(unityMcp.TryGetNode("args", out _), "unityMCP should contain args");
|
||||
|
||||
// Verify env.SystemRoot is present on Windows
|
||||
bool hasEnv = unityMcp.TryGetNode("env", out var envNode);
|
||||
Assert.IsTrue(hasEnv, "Windows config should contain env table");
|
||||
Assert.IsInstanceOf<TomlTable>(envNode, "env should be a table");
|
||||
|
||||
var env = envNode as TomlTable;
|
||||
Assert.IsTrue(env.TryGetNode("SystemRoot", out var systemRootNode), "env should contain SystemRoot");
|
||||
Assert.IsInstanceOf<TomlString>(systemRootNode, "SystemRoot should be a string");
|
||||
|
||||
var systemRoot = (systemRootNode as TomlString).Value;
|
||||
Assert.AreEqual("C:\\Windows", systemRoot, "SystemRoot should be C:\\Windows");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildCodexServerBlock_OnNonWindows_ExcludesEnv()
|
||||
{
|
||||
// This test verifies that non-Windows platforms don't include env configuration
|
||||
|
||||
// Mock non-Windows platform (e.g., macOS/Linux)
|
||||
MCPServiceLocator.Register<IPlatformService>(new MockPlatformService(isWindows: false));
|
||||
|
||||
string uvPath = "/usr/local/bin/uv";
|
||||
string serverSrc = "/path/to/server";
|
||||
|
||||
string result = CodexConfigHelper.BuildCodexServerBlock(uvPath, serverSrc);
|
||||
|
||||
Assert.IsNotNull(result, "BuildCodexServerBlock should return a valid TOML string");
|
||||
|
||||
// Parse the generated TOML to validate structure
|
||||
TomlTable parsed;
|
||||
using (var reader = new StringReader(result))
|
||||
{
|
||||
parsed = TOML.Parse(reader);
|
||||
}
|
||||
|
||||
// Verify basic structure
|
||||
Assert.IsTrue(parsed.TryGetNode("mcp_servers", out var mcpServersNode), "TOML should contain mcp_servers");
|
||||
Assert.IsInstanceOf<TomlTable>(mcpServersNode, "mcp_servers should be a table");
|
||||
|
||||
var mcpServers = mcpServersNode as TomlTable;
|
||||
Assert.IsTrue(mcpServers.TryGetNode("unityMCP", out var unityMcpNode), "mcp_servers should contain unityMCP");
|
||||
Assert.IsInstanceOf<TomlTable>(unityMcpNode, "unityMCP should be a table");
|
||||
|
||||
var unityMcp = unityMcpNode as TomlTable;
|
||||
Assert.IsTrue(unityMcp.TryGetNode("command", out _), "unityMCP should contain command");
|
||||
Assert.IsTrue(unityMcp.TryGetNode("args", out _), "unityMCP should contain args");
|
||||
|
||||
// Verify env is NOT present on non-Windows platforms
|
||||
bool hasEnv = unityMcp.TryGetNode("env", out _);
|
||||
Assert.IsFalse(hasEnv, "Non-Windows config should not contain env table");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UpsertCodexServerBlock_OnWindows_IncludesSystemRootEnv()
|
||||
{
|
||||
// This test verifies the fix for https://github.com/CoplayDev/unity-mcp/issues/315
|
||||
// Ensures that upsert operations also include Windows-specific env configuration
|
||||
|
||||
// Mock Windows platform
|
||||
MCPServiceLocator.Register<IPlatformService>(new MockPlatformService(isWindows: true, systemRoot: "C:\\Windows"));
|
||||
|
||||
string existingToml = string.Join("\n", new[]
|
||||
{
|
||||
"[other_section]",
|
||||
"key = \"value\""
|
||||
});
|
||||
|
||||
string uvPath = "C:\\path\\to\\uv.exe";
|
||||
string serverSrc = "C:\\path\\to\\server";
|
||||
|
||||
string result = CodexConfigHelper.UpsertCodexServerBlock(existingToml, uvPath, serverSrc);
|
||||
|
||||
Assert.IsNotNull(result, "UpsertCodexServerBlock should return a valid TOML string");
|
||||
|
||||
// Parse the generated TOML to validate structure
|
||||
TomlTable parsed;
|
||||
using (var reader = new StringReader(result))
|
||||
{
|
||||
parsed = TOML.Parse(reader);
|
||||
}
|
||||
|
||||
// Verify existing sections are preserved
|
||||
Assert.IsTrue(parsed.TryGetNode("other_section", out _), "TOML should preserve existing sections");
|
||||
|
||||
// Verify mcp_servers structure
|
||||
Assert.IsTrue(parsed.TryGetNode("mcp_servers", out var mcpServersNode), "TOML should contain mcp_servers");
|
||||
Assert.IsInstanceOf<TomlTable>(mcpServersNode, "mcp_servers should be a table");
|
||||
|
||||
var mcpServers = mcpServersNode as TomlTable;
|
||||
Assert.IsTrue(mcpServers.TryGetNode("unityMCP", out var unityMcpNode), "mcp_servers should contain unityMCP");
|
||||
Assert.IsInstanceOf<TomlTable>(unityMcpNode, "unityMCP should be a table");
|
||||
|
||||
var unityMcp = unityMcpNode as TomlTable;
|
||||
Assert.IsTrue(unityMcp.TryGetNode("command", out _), "unityMCP should contain command");
|
||||
Assert.IsTrue(unityMcp.TryGetNode("args", out _), "unityMCP should contain args");
|
||||
|
||||
// Verify env.SystemRoot is present on Windows
|
||||
bool hasEnv = unityMcp.TryGetNode("env", out var envNode);
|
||||
Assert.IsTrue(hasEnv, "Windows config should contain env table");
|
||||
Assert.IsInstanceOf<TomlTable>(envNode, "env should be a table");
|
||||
|
||||
var env = envNode as TomlTable;
|
||||
Assert.IsTrue(env.TryGetNode("SystemRoot", out var systemRootNode), "env should contain SystemRoot");
|
||||
Assert.IsInstanceOf<TomlString>(systemRootNode, "SystemRoot should be a string");
|
||||
|
||||
var systemRoot = (systemRootNode as TomlString).Value;
|
||||
Assert.AreEqual("C:\\Windows", systemRoot, "SystemRoot should be C:\\Windows");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UpsertCodexServerBlock_OnNonWindows_ExcludesEnv()
|
||||
{
|
||||
// This test verifies that upsert operations on non-Windows platforms don't include env configuration
|
||||
|
||||
// Mock non-Windows platform (e.g., macOS/Linux)
|
||||
MCPServiceLocator.Register<IPlatformService>(new MockPlatformService(isWindows: false));
|
||||
|
||||
string existingToml = string.Join("\n", new[]
|
||||
{
|
||||
"[other_section]",
|
||||
"key = \"value\""
|
||||
});
|
||||
|
||||
string uvPath = "/usr/local/bin/uv";
|
||||
string serverSrc = "/path/to/server";
|
||||
|
||||
string result = CodexConfigHelper.UpsertCodexServerBlock(existingToml, uvPath, serverSrc);
|
||||
|
||||
Assert.IsNotNull(result, "UpsertCodexServerBlock should return a valid TOML string");
|
||||
|
||||
// Parse the generated TOML to validate structure
|
||||
TomlTable parsed;
|
||||
using (var reader = new StringReader(result))
|
||||
{
|
||||
parsed = TOML.Parse(reader);
|
||||
}
|
||||
|
||||
// Verify existing sections are preserved
|
||||
Assert.IsTrue(parsed.TryGetNode("other_section", out _), "TOML should preserve existing sections");
|
||||
|
||||
// Verify mcp_servers structure
|
||||
Assert.IsTrue(parsed.TryGetNode("mcp_servers", out var mcpServersNode), "TOML should contain mcp_servers");
|
||||
Assert.IsInstanceOf<TomlTable>(mcpServersNode, "mcp_servers should be a table");
|
||||
|
||||
var mcpServers = mcpServersNode as TomlTable;
|
||||
Assert.IsTrue(mcpServers.TryGetNode("unityMCP", out var unityMcpNode), "mcp_servers should contain unityMCP");
|
||||
Assert.IsInstanceOf<TomlTable>(unityMcpNode, "unityMCP should be a table");
|
||||
|
||||
var unityMcp = unityMcpNode as TomlTable;
|
||||
Assert.IsTrue(unityMcp.TryGetNode("command", out _), "unityMCP should contain command");
|
||||
Assert.IsTrue(unityMcp.TryGetNode("args", out _), "unityMCP should contain args");
|
||||
|
||||
// Verify env is NOT present on non-Windows platforms
|
||||
bool hasEnv = unityMcp.TryGetNode("env", out _);
|
||||
Assert.IsFalse(hasEnv, "Non-Windows config should not contain env table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
namespace MCPForUnityTests.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for PackageLifecycleManager.
|
||||
/// Note: These tests verify the logic but cannot fully test [InitializeOnLoad] behavior.
|
||||
/// </summary>
|
||||
public class PackageLifecycleManagerTests
|
||||
{
|
||||
private const string TestVersionKey = "MCPForUnity.InstalledVersion:test-version";
|
||||
private const string LegacyInstallFlagKey = "MCPForUnity.ServerInstalled";
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
// Clean up test keys before each test
|
||||
CleanupTestKeys();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Clean up test keys after each test
|
||||
CleanupTestKeys();
|
||||
}
|
||||
|
||||
private void CleanupTestKeys()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (EditorPrefs.HasKey(TestVersionKey))
|
||||
{
|
||||
EditorPrefs.DeleteKey(TestVersionKey);
|
||||
}
|
||||
if (EditorPrefs.HasKey(LegacyInstallFlagKey))
|
||||
{
|
||||
EditorPrefs.DeleteKey(LegacyInstallFlagKey);
|
||||
}
|
||||
// Clean up any other test-related keys
|
||||
string[] testKeys = {
|
||||
"MCPForUnity.ServerSrc",
|
||||
"MCPForUnity.PythonDirOverride",
|
||||
"MCPForUnity.LegacyDetectLogged"
|
||||
};
|
||||
foreach (var key in testKeys)
|
||||
{
|
||||
if (EditorPrefs.HasKey(key))
|
||||
{
|
||||
EditorPrefs.DeleteKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FirstTimeInstall_ShouldNotHaveLegacyFlag()
|
||||
{
|
||||
// Verify that on a fresh install, the legacy flag doesn't exist
|
||||
Assert.IsFalse(EditorPrefs.HasKey(LegacyInstallFlagKey),
|
||||
"Fresh install should not have legacy installation flag");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VersionKey_ShouldBeVersionScoped()
|
||||
{
|
||||
// Verify that version keys are properly scoped
|
||||
string version1Key = "MCPForUnity.InstalledVersion:1.0.0";
|
||||
string version2Key = "MCPForUnity.InstalledVersion:2.0.0";
|
||||
|
||||
Assert.AreNotEqual(version1Key, version2Key,
|
||||
"Different versions should have different keys");
|
||||
Assert.IsTrue(version1Key.StartsWith("MCPForUnity.InstalledVersion:"),
|
||||
"Version key should have correct prefix");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LegacyPrefsCleanup_ShouldRemoveOldKeys()
|
||||
{
|
||||
// Set up legacy keys
|
||||
EditorPrefs.SetString("MCPForUnity.ServerSrc", "test");
|
||||
EditorPrefs.SetString("MCPForUnity.PythonDirOverride", "test");
|
||||
|
||||
// Verify they exist
|
||||
Assert.IsTrue(EditorPrefs.HasKey("MCPForUnity.ServerSrc"),
|
||||
"Legacy key should exist before cleanup");
|
||||
Assert.IsTrue(EditorPrefs.HasKey("MCPForUnity.PythonDirOverride"),
|
||||
"Legacy key should exist before cleanup");
|
||||
|
||||
// Note: We can't directly test the cleanup since it's private,
|
||||
// but we can verify the keys exist and document expected behavior
|
||||
// In actual usage, PackageLifecycleManager will clean these up
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VersionKeyFormat_ShouldFollowConvention()
|
||||
{
|
||||
// Test that version key format follows the expected pattern
|
||||
string testVersion = "1.2.3";
|
||||
string expectedKey = $"MCPForUnity.InstalledVersion:{testVersion}";
|
||||
|
||||
Assert.AreEqual("MCPForUnity.InstalledVersion:1.2.3", expectedKey,
|
||||
"Version key should follow format: prefix + version");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleVersions_ShouldHaveIndependentKeys()
|
||||
{
|
||||
// Simulate multiple version installations
|
||||
EditorPrefs.SetBool("MCPForUnity.InstalledVersion:1.0.0", true);
|
||||
EditorPrefs.SetBool("MCPForUnity.InstalledVersion:2.0.0", true);
|
||||
|
||||
Assert.IsTrue(EditorPrefs.GetBool("MCPForUnity.InstalledVersion:1.0.0"),
|
||||
"Version 1.0.0 flag should be set");
|
||||
Assert.IsTrue(EditorPrefs.GetBool("MCPForUnity.InstalledVersion:2.0.0"),
|
||||
"Version 2.0.0 flag should be set");
|
||||
|
||||
// Clean up
|
||||
EditorPrefs.DeleteKey("MCPForUnity.InstalledVersion:1.0.0");
|
||||
EditorPrefs.DeleteKey("MCPForUnity.InstalledVersion:2.0.0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LegacyFlagMigration_ShouldPreserveBackwardCompatibility()
|
||||
{
|
||||
// Simulate a scenario where old PackageInstaller set the flag
|
||||
EditorPrefs.SetBool(LegacyInstallFlagKey, true);
|
||||
|
||||
Assert.IsTrue(EditorPrefs.GetBool(LegacyInstallFlagKey),
|
||||
"Legacy flag should be readable for backward compatibility");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EditorPrefsKeys_ShouldNotConflict()
|
||||
{
|
||||
// Verify that our keys don't conflict with Unity or other packages
|
||||
string[] ourKeys = {
|
||||
"MCPForUnity.InstalledVersion:1.0.0",
|
||||
"MCPForUnity.ServerInstalled",
|
||||
"MCPForUnity.ServerSrc",
|
||||
"MCPForUnity.PythonDirOverride"
|
||||
};
|
||||
|
||||
foreach (var key in ourKeys)
|
||||
{
|
||||
Assert.IsTrue(key.StartsWith("MCPForUnity."),
|
||||
$"Key '{key}' should be properly namespaced");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VersionString_ShouldHandleUnknownGracefully()
|
||||
{
|
||||
// Test that "unknown" version is a valid fallback
|
||||
string unknownVersion = "unknown";
|
||||
string versionKey = $"MCPForUnity.InstalledVersion:{unknownVersion}";
|
||||
|
||||
Assert.IsNotNull(versionKey, "Version key should handle 'unknown' version");
|
||||
Assert.IsTrue(versionKey.Contains("unknown"),
|
||||
"Version key should contain the unknown version string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 59a2e40dd69724951bbd575569702ef1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 575ef6172fca24e4bbe5ecc1160691bb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using MCPForUnity.Editor.Helpers;
|
||||
using MCPForUnity.Editor.Models;
|
||||
|
||||
namespace MCPForUnityTests.Editor.Windows
|
||||
{
|
||||
public class ManualConfigJsonBuilderTests
|
||||
{
|
||||
[Test]
|
||||
public void VSCode_ManualJson_HasServers_NoEnv_NoDisabled()
|
||||
{
|
||||
var client = new McpClient { name = "VSCode", mcpType = McpTypes.VSCode };
|
||||
string json = ConfigJsonBuilder.BuildManualConfigJson("/usr/bin/uv", "/path/to/server", client);
|
||||
|
||||
var root = JObject.Parse(json);
|
||||
var unity = (JObject)root.SelectToken("servers.unityMCP");
|
||||
Assert.NotNull(unity, "Expected servers.unityMCP node");
|
||||
Assert.AreEqual("/usr/bin/uv", (string)unity["command"]);
|
||||
CollectionAssert.AreEqual(new[] { "run", "--directory", "/path/to/server", "server.py" }, unity["args"].ToObject<string[]>());
|
||||
Assert.AreEqual("stdio", (string)unity["type"], "VSCode should include type=stdio");
|
||||
Assert.IsNull(unity["env"], "env should not be added for VSCode");
|
||||
Assert.IsNull(unity["disabled"], "disabled should not be added for VSCode");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Windsurf_ManualJson_HasMcpServersEnv_DisabledFalse()
|
||||
{
|
||||
var client = new McpClient { name = "Windsurf", mcpType = McpTypes.Windsurf };
|
||||
string json = ConfigJsonBuilder.BuildManualConfigJson("/usr/bin/uv", "/path/to/server", client);
|
||||
|
||||
var root = JObject.Parse(json);
|
||||
var unity = (JObject)root.SelectToken("mcpServers.unityMCP");
|
||||
Assert.NotNull(unity, "Expected mcpServers.unityMCP node");
|
||||
Assert.NotNull(unity["env"], "env should be included");
|
||||
Assert.AreEqual(false, (bool)unity["disabled"], "disabled:false should be added for Windsurf");
|
||||
Assert.IsNull(unity["type"], "type should not be added for non-VSCode clients");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Cursor_ManualJson_HasMcpServers_NoEnv_NoDisabled()
|
||||
{
|
||||
var client = new McpClient { name = "Cursor", mcpType = McpTypes.Cursor };
|
||||
string json = ConfigJsonBuilder.BuildManualConfigJson("/usr/bin/uv", "/path/to/server", client);
|
||||
|
||||
var root = JObject.Parse(json);
|
||||
var unity = (JObject)root.SelectToken("mcpServers.unityMCP");
|
||||
Assert.NotNull(unity, "Expected mcpServers.unityMCP node");
|
||||
Assert.IsNull(unity["env"], "env should not be added for Cursor");
|
||||
Assert.IsNull(unity["disabled"], "disabled should not be added for Cursor");
|
||||
Assert.IsNull(unity["type"], "type should not be added for non-VSCode clients");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2dbc50071a45a4adc8e7a91a25bd4fd8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -450,7 +450,28 @@ namespace MCPForUnity.Editor
|
|||
}
|
||||
foreach (var c in toClose)
|
||||
{
|
||||
try { c.Close(); } catch { }
|
||||
try
|
||||
{
|
||||
// Properly shutdown before closing to avoid CloseWait state
|
||||
if (c.Client != null && c.Connected)
|
||||
{
|
||||
c.Client.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
c.Close();
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
// Expected: socket may already be closed
|
||||
if (IsDebugEnabled())
|
||||
{
|
||||
McpLog.Info($"Stop: socket shutdown {se.SocketErrorCode}", always: false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log unexpected errors during shutdown
|
||||
McpLog.Warn($"Error closing client connection: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Give the background loop a short window to exit without blocking the editor
|
||||
|
|
@ -512,10 +533,13 @@ namespace MCPForUnity.Editor
|
|||
|
||||
private static async Task HandleClientAsync(TcpClient client, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (clientsLock) { activeClients.Add(client); }
|
||||
|
||||
using (client)
|
||||
using (NetworkStream stream = client.GetStream())
|
||||
{
|
||||
lock (clientsLock) { activeClients.Add(client); }
|
||||
try
|
||||
{
|
||||
// Framed I/O only; legacy mode removed
|
||||
|
|
@ -674,9 +698,37 @@ namespace MCPForUnity.Editor
|
|||
}
|
||||
finally
|
||||
{
|
||||
lock (clientsLock) { activeClients.Remove(client); }
|
||||
// Properly shutdown the socket before disposal to avoid CloseWait
|
||||
try
|
||||
{
|
||||
if (client.Client != null && client.Connected)
|
||||
{
|
||||
client.Client.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
// Expected: socket may already be closed by remote
|
||||
if (IsDebugEnabled())
|
||||
{
|
||||
McpLog.Info($"Socket shutdown: {se.SocketErrorCode}", always: false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Unexpected errors should be logged
|
||||
if (IsDebugEnabled())
|
||||
{
|
||||
McpLog.Warn($"Error during socket shutdown: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (clientsLock) { activeClients.Remove(client); }
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout-aware exact read helper with cancellation; avoids indefinite stalls and background task leaks
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ First, create a ScriptableObject to manage your Python tools:
|
|||
2. Select **Assets > Create > MCP For Unity > Python Tools**
|
||||
3. Name it (e.g., `MyPythonTools`)
|
||||
|
||||

|
||||

|
||||
|
||||
## Step 2: Create Your Python Tool File
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ async def my_custom_tool(
|
|||
2. In the Inspector, expand **Python Files**
|
||||
3. Drag your `.py` file into the list (or click **+** and select it)
|
||||
|
||||

|
||||

|
||||
|
||||
**Note:** If you can't see `.py` files in the object picker, go to **Window > MCP For Unity > Tool Sync > Reimport Python Files** to force Unity to recognize them as text assets.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ pip install -e .[dev]
|
|||
```
|
||||
|
||||
This installs:
|
||||
- **Runtime dependencies**: `httpx`, `mcp[cli]`, `pydantic`, `tomli`
|
||||
|
||||
- **Runtime dependencies**: `httpx`, `mcp`, `pydantic`, `tomli`
|
||||
- **Development dependencies**: `pytest`, `pytest-anyio`
|
||||
|
||||
### Running Tests
|
||||
|
|
@ -31,6 +32,7 @@ pytest tests/ -v
|
|||
```
|
||||
|
||||
Or if you prefer using Python module syntax:
|
||||
|
||||
```bash
|
||||
python -m pytest tests/ -v
|
||||
```
|
||||
|
|
@ -38,9 +40,11 @@ python -m pytest tests/ -v
|
|||
## 🚀 Available Development Features
|
||||
|
||||
### ✅ Development Deployment Scripts
|
||||
|
||||
Quick deployment and testing tools for MCP for Unity core changes.
|
||||
|
||||
### 🔄 Coming Soon
|
||||
|
||||
- **Development Mode Toggle**: Built-in Unity editor development features
|
||||
- **Hot Reload System**: Real-time code updates without Unity restarts
|
||||
- **Plugin Development Kit**: Tools for creating custom MCP for Unity extensions
|
||||
|
|
@ -54,11 +58,13 @@ Quick deployment and testing tools for MCP for Unity core changes.
|
|||
Run this from the unity-mcp repo, not your game's root directory. Use `mcp_source.py` to quickly switch between different MCP for Unity package sources:
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
python mcp_source.py [--manifest /path/to/manifest.json] [--repo /path/to/unity-mcp] [--choice 1|2|3]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
- **1** Upstream main (CoplayDev/unity-mcp)
|
||||
- **2** Remote current branch (origin + branch)
|
||||
- **3** Local workspace (file: MCPForUnity)
|
||||
|
|
@ -72,14 +78,17 @@ These deployment scripts help you quickly test changes to MCP for Unity core cod
|
|||
## Scripts
|
||||
|
||||
### `deploy-dev.bat`
|
||||
|
||||
Deploys your development code to the actual installation locations for testing.
|
||||
|
||||
**What it does:**
|
||||
|
||||
1. Backs up original files to a timestamped folder
|
||||
2. Copies Unity Bridge code to Unity's package cache
|
||||
3. Copies Python Server code to the MCP installation folder
|
||||
|
||||
**Usage:**
|
||||
|
||||
1. Run `deploy-dev.bat`
|
||||
2. Enter Unity package cache path (example provided)
|
||||
3. Enter server path (or use default: `%LOCALAPPDATA%\Programs\UnityMCP\UnityMcpServer\src`)
|
||||
|
|
@ -88,17 +97,21 @@ Deploys your development code to the actual installation locations for testing.
|
|||
**Note:** Dev deploy skips `.venv`, `__pycache__`, `.pytest_cache`, `.mypy_cache`, `.git`; reduces churn and avoids copying virtualenvs.
|
||||
|
||||
### `restore-dev.bat`
|
||||
|
||||
Restores original files from backup.
|
||||
|
||||
**What it does:**
|
||||
|
||||
1. Lists available backups with timestamps
|
||||
2. Allows you to select which backup to restore
|
||||
3. Restores both Unity Bridge and Python Server files
|
||||
|
||||
### `prune_tool_results.py`
|
||||
|
||||
Compacts large `tool_result` blobs in conversation JSON into concise one-line summaries.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
python3 prune_tool_results.py < reports/claude-execution-output.json > reports/claude-execution-output.pruned.json
|
||||
```
|
||||
|
|
@ -120,6 +133,7 @@ X:\UnityProject\Library\PackageCache\com.coplaydev.unity-mcp@272123cfd97e
|
|||
```
|
||||
|
||||
To find it reliably:
|
||||
|
||||
1. Open Unity Package Manager
|
||||
2. Select "MCP for Unity" package
|
||||
3. Right click the package and choose "Show in Explorer"
|
||||
|
|
@ -132,9 +146,11 @@ Note: In recent builds, the Python server sources are also bundled inside the pa
|
|||
An on-demand stress utility exercises the MCP bridge with multiple concurrent clients while triggering real script reloads via immediate script edits (no menu calls required).
|
||||
|
||||
### Script
|
||||
|
||||
- `tools/stress_mcp.py`
|
||||
|
||||
### What it does
|
||||
|
||||
- Starts N TCP clients against the MCP for Unity bridge (default port auto-discovered from `~/.unity-mcp/unity-mcp-status-*.json`).
|
||||
- Sends lightweight framed `ping` keepalives to maintain concurrency.
|
||||
- In parallel, appends a unique marker comment to a target C# file using `manage_script.apply_text_edits` with:
|
||||
|
|
@ -143,6 +159,7 @@ An on-demand stress utility exercises the MCP bridge with multiple concurrent cl
|
|||
- Uses EOF insertion to avoid header/`using`-guard edits.
|
||||
|
||||
### Usage (local)
|
||||
|
||||
```bash
|
||||
# Recommended: use the included large script in the test project
|
||||
python3 tools/stress_mcp.py \
|
||||
|
|
@ -151,13 +168,15 @@ python3 tools/stress_mcp.py \
|
|||
--unity-file "TestProjects/UnityMCPTests/Assets/Scripts/LongUnityScriptClaudeTest.cs"
|
||||
```
|
||||
|
||||
Flags:
|
||||
### Flags
|
||||
|
||||
- `--project` Unity project path (auto-detected to the included test project by default)
|
||||
- `--unity-file` C# file to edit (defaults to the long test script)
|
||||
- `--clients` number of concurrent clients (default 10)
|
||||
- `--duration` seconds to run (default 60)
|
||||
|
||||
### Expected outcome
|
||||
|
||||
- No Unity Editor crashes during reload churn
|
||||
- Immediate reloads after each applied edit (no `Assets/Refresh` menu calls)
|
||||
- Some transient disconnects or a few failed calls may occur during domain reload; the tool retries and continues
|
||||
|
|
@ -165,6 +184,7 @@ Flags:
|
|||
- `{"port": 6400, "stats": {"pings": 28566, "applies": 69, "disconnects": 0, "errors": 0}}`
|
||||
|
||||
### Notes and troubleshooting
|
||||
|
||||
- Immediate vs debounced:
|
||||
- The tool sets `options.refresh = "immediate"` so changes compile instantly. If you only need churn (not per-edit confirmation), switch to debounced to reduce mid-reload failures.
|
||||
- Precondition required:
|
||||
|
|
@ -177,6 +197,7 @@ Flags:
|
|||
- Occasional `apply_errors` often indicate the connection reloaded mid-reply. Edits still typically apply; the loop continues on the next iteration.
|
||||
|
||||
### CI guidance
|
||||
|
||||
- Keep this out of default PR CI due to Unity/editor requirements and runtime variability.
|
||||
- Optionally run it as a manual workflow or nightly job on a Unity-capable runner.
|
||||
|
||||
|
|
@ -185,32 +206,38 @@ Flags:
|
|||
We provide a CI job to run a Natural Language Editing suite against the Unity test project. It spins up a headless Unity container and connects via the MCP bridge. To run from your fork, you need the following GitHub "secrets": an `ANTHROPIC_API_KEY` and Unity credentials (usually `UNITY_EMAIL` + `UNITY_PASSWORD` or `UNITY_LICENSE` / `UNITY_SERIAL`.) These are redacted in logs so never visible.
|
||||
|
||||
***To run it***
|
||||
- Trigger: In GitHun "Actions" for the repo, trigger `workflow dispatch` (`Claude NL/T Full Suite (Unity live)`).
|
||||
|
||||
- Trigger: In GitHub "Actions" for the repo, trigger `workflow dispatch` (`Claude NL/T Full Suite (Unity live)`).
|
||||
- Image: `UNITY_IMAGE` (UnityCI) pulled by tag; the job resolves a digest at runtime. Logs are sanitized.
|
||||
- Execution: single pass with immediate per‑test fragment emissions (strict single `<testcase>` per file). A placeholder guard fails fast if any fragment is a bare ID. Staging (`reports/_staging`) is promoted to `reports/` to reduce partial writes.
|
||||
- Reports: JUnit at `reports/junit-nl-suite.xml`, Markdown at `reports/junit-nl-suite.md`.
|
||||
- Publishing: JUnit is normalized to `reports/junit-for-actions.xml` and published; artifacts upload all files under `reports/`.
|
||||
|
||||
### Test target script
|
||||
|
||||
- The repo includes a long, standalone C# script used to exercise larger edits and windows:
|
||||
- `TestProjects/UnityMCPTests/Assets/Scripts/LongUnityScriptClaudeTest.cs`
|
||||
Use this file locally and in CI to validate multi-edit batches, anchor inserts, and windowed reads on a sizable script.
|
||||
|
||||
### Adjust tests / prompts
|
||||
|
||||
- Edit `.claude/prompts/nl-unity-suite-t.md` to modify the NL/T steps. Follow the conventions: emit one XML fragment per test under `reports/<TESTID>_results.xml`, each containing exactly one `<testcase>` with a `name` that begins with the test ID. No prologue/epilogue or code fences.
|
||||
- Keep edits minimal and reversible; include concise evidence.
|
||||
|
||||
### Run the suite
|
||||
|
||||
1) Push your branch, then manually run the workflow from the Actions tab.
|
||||
2) The job writes reports into `reports/` and uploads artifacts.
|
||||
3) The “JUnit Test Report” check summarizes results; open the Job Summary for full markdown.
|
||||
|
||||
### View results
|
||||
|
||||
- Job Summary: inline markdown summary of the run on the Actions tab in GitHub
|
||||
- Check: “JUnit Test Report” on the PR/commit.
|
||||
- Artifacts: `claude-nl-suite-artifacts` includes XML and MD.
|
||||
|
||||
### MCP Connection Debugging
|
||||
|
||||
- *Enable debug logs* in the MCP for Unity window (inside the Editor) to view connection status, auto-setup results, and MCP client paths. It shows:
|
||||
- bridge startup/port, client connections, strict framing negotiation, and parsed frames
|
||||
- auto-config path detection (Windows/macOS/Linux), uv/claude resolution, and surfaced errors
|
||||
|
|
@ -226,19 +253,23 @@ We provide a CI job to run a Natural Language Editing suite against the Unity te
|
|||
## Troubleshooting
|
||||
|
||||
### "Path not found" errors running the .bat file
|
||||
|
||||
- Verify Unity package cache path is correct
|
||||
- Check that MCP for Unity package is actually installed
|
||||
- Ensure server is installed via MCP client
|
||||
|
||||
### "Permission denied" errors
|
||||
|
||||
- Run cmd as Administrator
|
||||
- Close Unity Editor before deploying
|
||||
- Close any MCP clients before deploying
|
||||
|
||||
### "Backup not found" errors
|
||||
|
||||
- Run `deploy-dev.bat` first to create initial backup
|
||||
- Check backup directory permissions
|
||||
- Verify backup directory path is correct
|
||||
|
||||
### Windows uv path issues
|
||||
|
||||
- On Windows, when testing GUI clients, prefer the WinGet Links `uv.exe`; if multiple `uv.exe` exist, use "Choose `uv` Install Location" to pin the Links shim.
|
||||
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 834 KiB After Width: | Height: | Size: 834 KiB |
|
After Width: | Height: | Size: 616 KiB |
|
Before Width: | Height: | Size: 529 KiB After Width: | Height: | Size: 529 KiB |
|
Before Width: | Height: | Size: 335 KiB After Width: | Height: | Size: 335 KiB |
|
Before Width: | Height: | Size: 242 KiB After Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 407 KiB After Width: | Height: | Size: 407 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 330 KiB After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 402 KiB After Width: | Height: | Size: 402 KiB |
|
Before Width: | Height: | Size: 640 KiB After Width: | Height: | Size: 640 KiB |
|
Before Width: | Height: | Size: 636 KiB After Width: | Height: | Size: 636 KiB |
|
Before Width: | Height: | Size: 610 KiB After Width: | Height: | Size: 610 KiB |
|
|
@ -15,7 +15,7 @@ Version 5 introduces a new package structure. The package is now installed from
|
|||
3. Find **MCP for Unity** in the list
|
||||
4. Click the **Remove** button to uninstall the legacy package
|
||||
|
||||

|
||||

|
||||
|
||||
### Step 2: Install from the New Path
|
||||
|
||||
|
|
@ -24,18 +24,18 @@ Version 5 introduces a new package structure. The package is now installed from
|
|||
3. Enter the following URL: `https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity`
|
||||
4. Click **Add** to install the package
|
||||
|
||||

|
||||

|
||||
|
||||
### Step 3: Rebuild MCP Server
|
||||
|
||||
After installing the new package, you need to rebuild the MCP server:
|
||||
|
||||
1. In Unity, go to **Window > MCP for Unity > Open MCP Window**
|
||||

|
||||

|
||||
2. Click the **Rebuild MCP Server** button
|
||||

|
||||

|
||||
3. You should see a success message confirming the rebuild
|
||||

|
||||

|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
> **UI Toolkit-based window with service-oriented architecture**
|
||||
|
||||

|
||||

|
||||
*Dark theme*
|
||||
|
||||

|
||||

|
||||
*Light theme*
|
||||
|
||||
---
|
||||
|
|
@ -56,7 +56,7 @@ The new MCP Editor Window is a complete rebuild using **UI Toolkit (UXML/USS)**
|
|||
- **Server Download Button** - Asset Store users can download the server from GitHub releases
|
||||
- **Dynamic UI** - Shows appropriate button based on installation type
|
||||
|
||||

|
||||

|
||||
*Asset Store version showing the "Download & Install Server" button*
|
||||
|
||||
---
|
||||
|
|
|
|||