Skip to main content
Glama

Unity-MCP

by TSavo
03-async-execution-mcp.md26.5 kB
# Asynchronous Execution in Unity-AI Bridge with MCP ## 1. Introduction ### 1.1 Purpose This specification defines the asynchronous execution model for the Unity-AI Bridge with MCP compatibility. It enables handling of long-running operations without blocking the AI assistant, allowing for more complex interactions with Unity while maintaining responsiveness. ### 1.2 Scope This document covers: - The asynchronous execution model - Timeout handling and configuration - Log ID generation and management - Result retrieval mechanisms - AI assistant interaction patterns - Implementation details ### 1.3 Prerequisites This specification assumes familiarity with: - The Model Context Protocol (MCP) as described in "01-model-context-protocol.md" - The Unity-AI Bridge as described in "02-unity-ai-bridge-mcp.md" - Basic understanding of asynchronous programming patterns ## 2. Asynchronous Execution Model ### 2.1 Overview The asynchronous execution model allows the Unity-AI Bridge to handle long-running operations without blocking the AI assistant. The key components of this model are: 1. **Timeout Parameters**: Allow the AI to specify how long it's willing to wait for a result 2. **Log IDs**: Unique identifiers for each operation that allow results to be retrieved later 3. **Result Storage**: A system for storing operation results, including partial results 4. **Result Retrieval**: A mechanism for retrieving results using Log IDs 5. **Status Updates**: A way to check the status of ongoing operations ### 2.2 Execution Flow The asynchronous execution flow follows these steps: 1. The AI assistant initiates an operation with a timeout parameter 2. The Unity-AI Bridge generates a Log ID for the operation 3. The operation is executed until completion or timeout 4. If the operation completes within the timeout, the result is returned immediately 5. If the operation times out, a partial result and status are returned 6. The AI assistant can retrieve the full result later using the Log ID ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ AI Assistant │ │ Unity-AI Bridge │ │ Unity Runtime │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ Request Operation │ │ │ with Timeout │ │ │───────────────────────► │ │ │ │ │ │ Generate Log ID │ │ │◄──────────────────────► │ │ │ │ │ Execute Operation │ │ │───────────────────────► │ │ │ │ │ │ │ │ If Timeout │ │ Partial Result with │◄──────────────────────┘ │ Log ID │ │◄──────────────────────┤ │ │ │ Request Result │ │ with Log ID │ │───────────────────────► │ │ │ Complete Result │ │◄──────────────────────┤ │ │ ``` ### 2.3 Key Benefits - **Responsiveness**: The AI assistant can continue interacting with the user while operations complete - **Complex Operations**: Enables operations that take longer than typical request timeouts - **Partial Results**: Provides feedback even for incomplete operations - **Resumability**: Operations can be monitored and results retrieved when ready - **Reliability**: Prevents lost results due to connection issues or timeouts ## 3. Timeout Handling ### 3.1 Timeout Parameter Each operation accepts a `timeout` parameter that specifies the maximum time (in seconds) the AI assistant is willing to wait for a result: ```json { "tool_id": "execute_code", "parameters": { "code": "// Some long-running code", "timeout": 5 } } ``` ### 3.2 Default Timeout If no timeout is specified, a default timeout of 1 second is used. This ensures that operations don't block the AI assistant for too long by default. ### 3.3 Timeout Behavior When a timeout occurs: 1. The operation continues running in the background 2. A partial result (if available) is returned to the AI assistant 3. The operation's status is set to "running" 4. The Log ID is included in the response for later result retrieval ### 3.4 Timeout Response When an operation times out, the response follows this format: ```json { "status": "timeout", "log_id": "550e8400-e29b-41d4-a716-446655440000", "partial_result": { // Any partial results available at timeout }, "message": "Operation timed out after 5 seconds. Use the get_operation_result tool with the provided log_id to retrieve the complete result when available." } ``` ## 4. Log ID Management ### 4.1 Log ID Generation Each operation is assigned a unique Log ID when it is initiated. The Log ID is a UUID (Universally Unique Identifier) that is used to identify the operation and retrieve its results later. ```csharp public string GenerateLogId() { return Guid.NewGuid().ToString(); } ``` ### 4.2 Log Storage The Unity-AI Bridge maintains a log storage system that associates Log IDs with operation results: ```csharp public class LogStorage { private Dictionary<string, OperationLog> logs = new Dictionary<string, OperationLog>(); public void StoreLog(string logId, OperationLog log) { logs[logId] = log; } public OperationLog GetLog(string logId) { if (logs.TryGetValue(logId, out var log)) { return log; } return null; } public void UpdateLog(string logId, OperationLog log) { if (logs.ContainsKey(logId)) { logs[logId] = log; } } public void CleanupOldLogs(TimeSpan maxAge) { var cutoffTime = DateTime.UtcNow - maxAge; var keysToRemove = logs.Where(kvp => kvp.Value.Timestamp < cutoffTime) .Select(kvp => kvp.Key) .ToList(); foreach (var key in keysToRemove) { logs.Remove(key); } } } ``` ### 4.3 Operation Log Structure Each operation log contains: ```csharp public class OperationLog { public string LogId { get; set; } public string ToolId { get; set; } public object Parameters { get; set; } public string Status { get; set; } // "running", "completed", "error" public object Result { get; set; } public object PartialResult { get; set; } public string ErrorMessage { get; set; } public DateTime Timestamp { get; set; } public DateTime LastUpdated { get; set; } } ``` ### 4.4 Log Retention Logs are retained for a configurable period (default: 24 hours) to allow for result retrieval. After this period, logs are automatically cleaned up to prevent memory leaks. ## 5. Result Retrieval ### 5.1 Result Retrieval Tool The Unity-AI Bridge provides a dedicated tool for retrieving operation results using Log IDs: ```json { "id": "get_operation_result", "description": "Retrieve the result of a previously initiated operation using its Log ID", "parameters": { "type": "object", "properties": { "log_id": { "type": "string", "description": "The Log ID of the operation" }, "wait": { "type": "boolean", "description": "Whether to wait for the operation to complete", "default": false }, "timeout": { "type": "number", "description": "Maximum time to wait in seconds (only used if wait is true)", "default": 5 } }, "required": ["log_id"] } } ``` ### 5.2 Result Retrieval Response The response from the result retrieval tool follows this format: ```json { "status": "completed", // "completed", "running", "error", "not_found" "log_id": "550e8400-e29b-41d4-a716-446655440000", "result": { // The operation result if completed }, "partial_result": { // Any partial results if still running }, "error": "Error message if status is error", "message": "Operation completed successfully." } ``` ### 5.3 Wait Parameter The `wait` parameter allows the AI assistant to wait for an operation to complete: - If `wait` is `true`, the tool will wait up to `timeout` seconds for the operation to complete - If `wait` is `false` (default), the tool will return immediately with the current status ### 5.4 Status Checking The AI assistant can check the status of an operation without retrieving the full result: ```json { "id": "get_operation_status", "description": "Check the status of a previously initiated operation", "parameters": { "type": "object", "properties": { "log_id": { "type": "string", "description": "The Log ID of the operation" } }, "required": ["log_id"] } } ``` ## 6. Implementation Details ### 6.1 Asynchronous Execution The Unity-AI Bridge implements asynchronous execution using Tasks and coroutines: ```csharp public async Task<object> ExecuteOperationAsync(string toolId, object parameters, int timeout) { // Generate a Log ID string logId = GenerateLogId(); // Create an operation log var log = new OperationLog { LogId = logId, ToolId = toolId, Parameters = parameters, Status = "running", Timestamp = DateTime.UtcNow, LastUpdated = DateTime.UtcNow }; // Store the log logStorage.StoreLog(logId, log); // Create a cancellation token source with the timeout var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)); try { // Execute the operation var task = ExecuteToolAsync(toolId, parameters, cts.Token); // Wait for the operation to complete or timeout if (await Task.WhenAny(task, Task.Delay(timeout * 1000)) == task) { // Operation completed within timeout var result = await task; // Update the log log.Status = "completed"; log.Result = result; log.LastUpdated = DateTime.UtcNow; logStorage.UpdateLog(logId, log); // Return the result with the Log ID return new { status = "completed", log_id = logId, result = result, message = "Operation completed successfully." }; } else { // Operation timed out cts.Cancel(); // Get any partial results var partialResult = GetPartialResult(toolId, parameters); // Update the log log.Status = "running"; log.PartialResult = partialResult; log.LastUpdated = DateTime.UtcNow; logStorage.UpdateLog(logId, log); // Return timeout response with the Log ID return new { status = "timeout", log_id = logId, partial_result = partialResult, message = $"Operation timed out after {timeout} seconds. Use the get_operation_result tool with the provided log_id to retrieve the complete result when available." }; } } catch (Exception ex) { // Operation failed log.Status = "error"; log.ErrorMessage = ex.Message; log.LastUpdated = DateTime.UtcNow; logStorage.UpdateLog(logId, log); // Return error response with the Log ID return new { status = "error", log_id = logId, error = ex.Message, message = "Operation failed with an error." }; } } ``` ### 6.2 Result Retrieval Implementation The implementation of the result retrieval tool: ```csharp public async Task<object> GetOperationResult(string logId, bool wait, int timeout) { // Get the operation log var log = logStorage.GetLog(logId); if (log == null) { return new { status = "not_found", log_id = logId, message = "Operation not found. The Log ID may be invalid or the log may have been cleaned up." }; } // If the operation is already completed or failed, return the result immediately if (log.Status == "completed" || log.Status == "error") { return new { status = log.Status, log_id = logId, result = log.Result, error = log.ErrorMessage, message = log.Status == "completed" ? "Operation completed successfully." : "Operation failed with an error." }; } // If wait is false, return the current status immediately if (!wait) { return new { status = log.Status, log_id = logId, partial_result = log.PartialResult, message = "Operation is still running. Use the get_operation_result tool with wait=true to wait for completion." }; } // Wait for the operation to complete var startTime = DateTime.UtcNow; while (log.Status == "running" && (DateTime.UtcNow - startTime).TotalSeconds < timeout) { await Task.Delay(100); log = logStorage.GetLog(logId); if (log.Status != "running") { break; } } // Check if the operation completed within the timeout if (log.Status == "completed" || log.Status == "error") { return new { status = log.Status, log_id = logId, result = log.Result, error = log.ErrorMessage, message = log.Status == "completed" ? "Operation completed successfully." : "Operation failed with an error." }; } else { return new { status = "running", log_id = logId, partial_result = log.PartialResult, message = "Operation is still running. Try again later or use a longer timeout." }; } } ``` ### 6.3 Partial Result Collection For operations that can produce partial results, the Unity-AI Bridge implements a mechanism to collect and store these results: ```csharp public object GetPartialResult(string toolId, object parameters) { switch (toolId) { case "execute_code": return GetCodeExecutionPartialResult(parameters); case "run_test": case "run_all_tests": return GetTestRunPartialResult(parameters); default: return null; } } private object GetCodeExecutionPartialResult(object parameters) { // Get any output or logs from the code execution var logs = codeExecutor.GetCurrentLogs(); return new { logs = logs, execution_status = "in_progress" }; } private object GetTestRunPartialResult(object parameters) { // Get any test results that have completed so far var completedTests = testRunner.GetCompletedTests(); return new { completed_tests = completedTests, total_tests = testRunner.GetTotalTests(), completed_count = completedTests.Count, test_run_status = "in_progress" }; } ``` ### 6.4 Background Task Management The Unity-AI Bridge manages background tasks to ensure they don't interfere with Unity's main thread: ```csharp public class BackgroundTaskManager : MonoBehaviour { private List<Task> activeTasks = new List<Task>(); private List<Task> completedTasks = new List<Task>(); private void Update() { // Check for completed tasks for (int i = activeTasks.Count - 1; i >= 0; i--) { var task = activeTasks[i]; if (task.IsCompleted) { completedTasks.Add(task); activeTasks.RemoveAt(i); } } // Clean up completed tasks if (completedTasks.Count > 100) { completedTasks.Clear(); } } public void RegisterTask(Task task) { activeTasks.Add(task); } public void CancelAllTasks() { // Cancel all active tasks } private void OnDestroy() { CancelAllTasks(); } } ``` ## 7. AI Assistant Interaction ### 7.1 Timeout Handling When an operation times out, the AI assistant should: 1. Acknowledge the timeout to the user 2. Store the Log ID for later use 3. Continue with other tasks or information gathering 4. Check for the result later using the Log ID ### 7.2 Result Retrieval The AI assistant can retrieve results in several ways: 1. **Immediate Check**: Check if a quick operation has completed 2. **Delayed Check**: Wait a specific amount of time before checking 3. **Periodic Checks**: Check at regular intervals for long-running operations 4. **Final Check**: Check at the end of a conversation or task ### 7.3 Example Interaction Here's an example of how an AI assistant should interact with the asynchronous execution model: ``` User: Can you run all the tests in my Unity project and tell me which ones are failing? AI: I'll run all the tests in your Unity project. This might take a while, so I'll start the tests and then check the results. [AI uses run_all_tests tool with a timeout of 5 seconds] AI: I've started running all the tests in your Unity project. The operation has timed out after 5 seconds, which means the tests are still running. I'll check the results in a moment. In the meantime, is there anything specific you're concerned about in the tests? Any particular area of the code you're focusing on? User: I'm particularly concerned about the Fighter tests. AI: I understand you're particularly concerned about the Fighter tests. Let me check if the test results are available now. [AI uses get_operation_result tool with the Log ID] AI: The tests are still running. So far, 15 out of 42 tests have completed. Of the completed tests, 12 have passed and 3 have failed. Let me check specifically for any Fighter tests that have completed: [AI analyzes the partial results for Fighter tests] AI: Among the completed tests, I can see that 2 Fighter tests have run: 1. FighterTests.Constructor_SetsPropertiesCorrectly - PASSED 2. FighterTests.WithDescription_SetsDescription - FAILED The failure in WithDescription_SetsDescription appears to be related to a null reference. Let me check again in a moment for more complete results. [AI uses get_operation_result tool with the Log ID again] AI: Great! All the tests have completed now. Here's a summary of the Fighter tests: PASSED (4): - FighterTests.Constructor_SetsPropertiesCorrectly - FighterTests.WithAudioFile_SetsAudioFile - FighterTests.WithImagePath_SetsImagePath - FighterTests.WithImagePrompt_SetsImagePrompt FAILED (1): - FighterTests.WithDescription_SetsDescription The failure in WithDescription_SetsDescription is due to: "NullReferenceException: Object reference not set to an instance of an object at TrolleyGame.Fighter.WithDescription(String description)" This suggests that there might be an issue with the Fighter.WithDescription method. Would you like me to look at the implementation of this method to identify the problem? ``` ### 7.4 Best Practices for AI Assistants AI assistants should follow these best practices when working with asynchronous operations: 1. **Set Appropriate Timeouts**: Use shorter timeouts for simple operations and longer timeouts for complex operations 2. **Inform the User**: Let the user know when an operation is running in the background 3. **Continue Interaction**: Don't block the conversation while waiting for results 4. **Check Periodically**: Check for results at appropriate intervals 5. **Provide Partial Information**: Share partial results when available 6. **Handle Errors Gracefully**: Inform the user if an operation fails and suggest alternatives ## 8. MCP Integration ### 8.1 MCP Tool Definitions The asynchronous execution model is exposed through MCP with these tool definitions: ```json { "id": "execute_code", "description": "Execute C# code in Unity at runtime", "parameters": { "type": "object", "properties": { "code": { "type": "string", "description": "C# code to execute" }, "timeout": { "type": "number", "description": "Maximum time to wait in seconds", "default": 1 } }, "required": ["code"] } } ``` ```json { "id": "get_operation_result", "description": "Retrieve the result of a previously initiated operation using its Log ID", "parameters": { "type": "object", "properties": { "log_id": { "type": "string", "description": "The Log ID of the operation" }, "wait": { "type": "boolean", "description": "Whether to wait for the operation to complete", "default": false }, "timeout": { "type": "number", "description": "Maximum time to wait in seconds (only used if wait is true)", "default": 5 } }, "required": ["log_id"] } } ``` ```json { "id": "get_operation_status", "description": "Check the status of a previously initiated operation", "parameters": { "type": "object", "properties": { "log_id": { "type": "string", "description": "The Log ID of the operation" } }, "required": ["log_id"] } } ``` ### 8.2 MCP Response Format All MCP responses include the Log ID and status information: ```json { "status": "completed", // "completed", "running", "error", "timeout", "not_found" "log_id": "550e8400-e29b-41d4-a716-446655440000", "result": { // The operation result if completed }, "partial_result": { // Any partial results if still running or timed out }, "error": "Error message if status is error", "message": "Human-readable message about the operation status" } ``` ## 9. Configuration ### 9.1 Default Timeout The default timeout for operations is 1 second. This can be configured globally: ```csharp public class UnityAIBridgeConfig : ScriptableObject { [SerializeField] private int defaultTimeoutSeconds = 1; [SerializeField] private int maxTimeoutSeconds = 60; [SerializeField] private int logRetentionHours = 24; // Getters and setters } ``` ### 9.2 Maximum Timeout To prevent abuse, a maximum timeout can be configured. Any timeout value larger than this will be capped: ```csharp public int GetEffectiveTimeout(int requestedTimeout) { return Math.Min(requestedTimeout, config.MaxTimeoutSeconds); } ``` ### 9.3 Log Retention The log retention period determines how long operation logs are kept before being cleaned up: ```csharp public void CleanupOldLogs() { var maxAge = TimeSpan.FromHours(config.LogRetentionHours); logStorage.CleanupOldLogs(maxAge); } ``` ## 10. Security Considerations ### 10.1 Resource Consumption Long-running operations can consume significant resources. To mitigate this: 1. **Operation Limits**: Limit the number of concurrent operations 2. **Resource Monitoring**: Monitor CPU, memory, and other resource usage 3. **Cancellation**: Provide a way to cancel long-running operations 4. **Timeouts**: Enforce maximum timeouts for all operations ### 10.2 Log ID Security Log IDs provide access to operation results. To secure them: 1. **Use UUIDs**: Generate cryptographically strong UUIDs 2. **Limited Lifetime**: Enforce log retention policies 3. **Access Control**: Restrict access to logs based on the originating session 4. **Sanitization**: Ensure logs don't contain sensitive information ## 11. Conclusion The asynchronous execution model for the Unity-AI Bridge with MCP compatibility enables AI assistants to work with long-running operations in Unity without blocking the conversation. By implementing timeouts, Log IDs, and result retrieval mechanisms, the bridge provides a flexible and responsive way to interact with Unity. This model is particularly valuable for operations like running tests, executing complex code, or performing resource-intensive tasks that may take longer than typical request timeouts. The ability to retrieve partial results and check operation status allows AI assistants to provide feedback and continue the conversation while operations complete in the background. By following the best practices outlined in this specification, AI assistants can provide a smooth and responsive experience when working with Unity, even when performing complex and time-consuming operations. ## 12. References - [Model Context Protocol Specification](./01-model-context-protocol.md) - [Unity-AI Bridge with MCP Compatibility Specification](./02-unity-ai-bridge-mcp.md) - [Unity Documentation on Coroutines](https://docs.unity3d.com/Manual/Coroutines.html) - [C# Task-based Asynchronous Pattern](https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TSavo/Unity-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server