Save current state before fixing MCP tool registration and proxying
This commit is contained in:
@@ -36,7 +36,7 @@ When you create duplicate files:
|
|||||||
|
|
||||||
## The Human Is Right
|
## The Human Is Right
|
||||||
|
|
||||||
The human specifically said: "I fucking hate it when you do this retarded shit and recreate the same file with some adjective/verb but leave the original"
|
The human specifically said: "Do not re-create the same file with some adjective/verb attached while leaving the original, instead, update the code and make it better, changes are good."
|
||||||
|
|
||||||
**Listen to them.** They prefer file replacement over duplicates.
|
**Listen to them.** They prefer file replacement over duplicates.
|
||||||
|
|
||||||
|
|||||||
@@ -255,8 +255,6 @@ defmodule AgentCoordinator do
|
|||||||
Map.get(server_status, :active_servers, 0)
|
Map.get(server_status, :active_servers, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp count_active_servers(_), do: 0
|
|
||||||
|
|
||||||
defp get_server_status do
|
defp get_server_status do
|
||||||
# This would call UnifiedMCPServer to get external server status
|
# This would call UnifiedMCPServer to get external server status
|
||||||
# For now, return a placeholder
|
# For now, return a placeholder
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ defmodule AgentCoordinator.Client do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use GenServer
|
use GenServer
|
||||||
alias AgentCoordinator.{EnhancedMCPServer, AutoHeartbeat}
|
alias AgentCoordinator.AutoHeartbeat
|
||||||
|
|
||||||
defstruct [
|
defstruct [
|
||||||
:agent_id,
|
:agent_id,
|
||||||
@@ -108,11 +108,11 @@ defmodule AgentCoordinator.Client do
|
|||||||
# Server callbacks
|
# Server callbacks
|
||||||
|
|
||||||
def init(config) do
|
def init(config) do
|
||||||
# Register with enhanced MCP server
|
# Register with task registry
|
||||||
case EnhancedMCPServer.register_agent_with_session(
|
case AgentCoordinator.TaskRegistry.register_agent(
|
||||||
config.agent_name,
|
config.agent_name,
|
||||||
config.capabilities,
|
config.capabilities,
|
||||||
self()
|
session_pid: self()
|
||||||
) do
|
) do
|
||||||
{:ok, agent_id} ->
|
{:ok, agent_id} ->
|
||||||
state = %__MODULE__{
|
state = %__MODULE__{
|
||||||
@@ -186,9 +186,9 @@ defmodule AgentCoordinator.Client do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_call(:get_task_board, _from, state) do
|
def handle_call(:get_task_board, _from, state) do
|
||||||
case EnhancedMCPServer.get_enhanced_task_board() do
|
case AgentCoordinator.TaskRegistry.get_task_board() do
|
||||||
{:ok, board} ->
|
task_board when is_map(task_board) ->
|
||||||
{:reply, {:ok, board}, update_last_heartbeat(state)}
|
{:reply, {:ok, task_board}, update_last_heartbeat(state)}
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:reply, {:error, reason}, state}
|
{:reply, {:error, reason}, state}
|
||||||
@@ -270,12 +270,10 @@ defmodule AgentCoordinator.Client do
|
|||||||
# Private helpers
|
# Private helpers
|
||||||
|
|
||||||
defp enhanced_mcp_call(request, state) do
|
defp enhanced_mcp_call(request, state) do
|
||||||
session_info = %{
|
# Add agent_id to the request for the MCP server
|
||||||
agent_id: state.agent_id,
|
request_with_agent = Map.put(request, "agent_id", state.agent_id)
|
||||||
session_pid: state.session_pid
|
|
||||||
}
|
|
||||||
|
|
||||||
case EnhancedMCPServer.handle_enhanced_mcp_request(request, session_info) do
|
case AgentCoordinator.MCPServer.handle_mcp_request(request_with_agent) do
|
||||||
%{"result" => %{"content" => [%{"text" => response_json}]}} = response ->
|
%{"result" => %{"content" => [%{"text" => response_json}]}} = response ->
|
||||||
case Jason.decode(response_json) do
|
case Jason.decode(response_json) do
|
||||||
{:ok, data} ->
|
{:ok, data} ->
|
||||||
@@ -304,7 +302,7 @@ defmodule AgentCoordinator.Client do
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case EnhancedMCPServer.handle_enhanced_mcp_request(request) do
|
case AgentCoordinator.MCPServer.handle_mcp_request(request) do
|
||||||
%{"result" => _} -> :ok
|
%{"result" => _} -> :ok
|
||||||
%{"error" => %{"message" => message}} -> {:error, message}
|
%{"error" => %{"message" => message}} -> {:error, message}
|
||||||
_ -> {:error, :unknown_heartbeat_error}
|
_ -> {:error, :unknown_heartbeat_error}
|
||||||
|
|||||||
@@ -51,13 +51,14 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"id" => %{"type" => "string"},
|
"id" => %{"type" => "string"},
|
||||||
"name" => %{"type" => "string"},
|
"name" => %{"type" => "string"},
|
||||||
"workspace_path" => %{"type" => "string"},
|
"workspace_path" => %{"type" => "string"},
|
||||||
"description" => %{"type" => "string"},
|
"description" => %{"type" => "string"},
|
||||||
"metadata" => %{"type" => "object"}
|
"metadata" => %{"type" => "object"}
|
||||||
},
|
},
|
||||||
"required" => ["name", "workspace_path"]
|
"required" => ["agent_id", "name", "workspace_path"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -66,6 +67,7 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"title" => %{"type" => "string"},
|
"title" => %{"type" => "string"},
|
||||||
"description" => %{"type" => "string"},
|
"description" => %{"type" => "string"},
|
||||||
"priority" => %{"type" => "string", "enum" => ["low", "normal", "high", "urgent"]},
|
"priority" => %{"type" => "string", "enum" => ["low", "normal", "high", "urgent"]},
|
||||||
@@ -86,7 +88,7 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required" => ["title", "description"]
|
"required" => ["agent_id", "title", "description"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -95,6 +97,7 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"title" => %{"type" => "string"},
|
"title" => %{"type" => "string"},
|
||||||
"description" => %{"type" => "string"},
|
"description" => %{"type" => "string"},
|
||||||
"primary_codebase_id" => %{"type" => "string"},
|
"primary_codebase_id" => %{"type" => "string"},
|
||||||
@@ -107,7 +110,7 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"enum" => ["sequential", "parallel", "leader_follower"]
|
"enum" => ["sequential", "parallel", "leader_follower"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required" => ["title", "description", "primary_codebase_id", "affected_codebases"]
|
"required" => ["agent_id", "title", "description", "primary_codebase_id", "affected_codebases"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -138,8 +141,10 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"codebase_id" => %{"type" => "string"}
|
"codebase_id" => %{"type" => "string"}
|
||||||
}
|
},
|
||||||
|
"required" => ["agent_id"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -148,9 +153,10 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"codebase_id" => %{"type" => "string"}
|
"codebase_id" => %{"type" => "string"}
|
||||||
},
|
},
|
||||||
"required" => ["codebase_id"]
|
"required" => ["agent_id", "codebase_id"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -158,7 +164,10 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"description" => "List all registered codebases",
|
"description" => "List all registered codebases",
|
||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{}
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"}
|
||||||
|
},
|
||||||
|
"required" => ["agent_id"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -167,12 +176,13 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"source_codebase_id" => %{"type" => "string"},
|
"source_codebase_id" => %{"type" => "string"},
|
||||||
"target_codebase_id" => %{"type" => "string"},
|
"target_codebase_id" => %{"type" => "string"},
|
||||||
"dependency_type" => %{"type" => "string"},
|
"dependency_type" => %{"type" => "string"},
|
||||||
"metadata" => %{"type" => "object"}
|
"metadata" => %{"type" => "object"}
|
||||||
},
|
},
|
||||||
"required" => ["source_codebase_id", "target_codebase_id", "dependency_type"]
|
"required" => ["agent_id", "source_codebase_id", "target_codebase_id", "dependency_type"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -282,6 +292,7 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"inputSchema" => %{
|
"inputSchema" => %{
|
||||||
"type" => "object",
|
"type" => "object",
|
||||||
"properties" => %{
|
"properties" => %{
|
||||||
|
"agent_id" => %{"type" => "string"},
|
||||||
"codebase_id" => %{
|
"codebase_id" => %{
|
||||||
"type" => "string",
|
"type" => "string",
|
||||||
"description" => "Optional: filter by codebase ID"
|
"description" => "Optional: filter by codebase ID"
|
||||||
@@ -291,7 +302,8 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
"default" => true,
|
"default" => true,
|
||||||
"description" => "Include full task details"
|
"description" => "Include full task details"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"required" => ["agent_id"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
@@ -383,14 +395,23 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
# Extract agent context for automatic heartbeat management
|
# Extract agent context for automatic heartbeat management
|
||||||
case extract_agent_context(request, from, state) do
|
case extract_agent_context(request, from, state) do
|
||||||
{:error, error_message} ->
|
{:error, error_message} ->
|
||||||
# Return error if agent context extraction fails (unless this is register_agent)
|
# Return error if agent context extraction fails (unless this is an allowed system call)
|
||||||
method = Map.get(request, "method")
|
method = Map.get(request, "method")
|
||||||
if method == "tools/call" and
|
tool_name = Map.get(request, "params", %{}) |> Map.get("name")
|
||||||
Map.get(request, "params", %{}) |> Map.get("name") == "register_agent" do
|
|
||||||
# Allow register_agent to proceed without agent_id
|
# Allow certain MCP system calls and register_agent to proceed without agent_id
|
||||||
|
allowed_without_agent = method in ["initialize", "tools/list", "notifications/initialized"] or
|
||||||
|
(method == "tools/call" and tool_name == "register_agent")
|
||||||
|
|
||||||
|
IO.puts(:stderr, "#{method} #{inspect(request)} #{tool_name}")
|
||||||
|
|
||||||
|
if allowed_without_agent do
|
||||||
|
# Allow system calls and register_agent to proceed without agent_id
|
||||||
response = process_mcp_request(request)
|
response = process_mcp_request(request)
|
||||||
{:reply, response, state}
|
{:reply, response, state}
|
||||||
else
|
else
|
||||||
|
# Log the rejected call for debugging
|
||||||
|
Logger.warning("Rejected call without agent_id: method=#{method}, tool=#{tool_name}")
|
||||||
error_response = %{
|
error_response = %{
|
||||||
"jsonrpc" => "2.0",
|
"jsonrpc" => "2.0",
|
||||||
"id" => Map.get(request, "id"),
|
"id" => Map.get(request, "id"),
|
||||||
@@ -402,6 +423,11 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
{:reply, error_response, state}
|
{:reply, error_response, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
%{agent_id: nil} ->
|
||||||
|
# System call without agent context
|
||||||
|
response = process_mcp_request(request)
|
||||||
|
{:reply, response, state}
|
||||||
|
|
||||||
agent_context ->
|
agent_context ->
|
||||||
# Send pre-operation heartbeat if we have agent context
|
# Send pre-operation heartbeat if we have agent context
|
||||||
if agent_context[:agent_id] do
|
if agent_context[:agent_id] do
|
||||||
@@ -478,6 +504,25 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp process_mcp_request(%{"method" => "notifications/initialized"} = request) do
|
||||||
|
# Handle the initialized notification - this is sent by clients after initialization
|
||||||
|
id = Map.get(request, "id", nil)
|
||||||
|
|
||||||
|
Logger.info("Client initialization notification received")
|
||||||
|
|
||||||
|
# For notifications, we typically don't send a response, but if there's an ID, respond
|
||||||
|
if id do
|
||||||
|
%{
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => id,
|
||||||
|
"result" => %{"status" => "acknowledged"}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# For notifications without ID, no response is expected
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp process_mcp_request(
|
defp process_mcp_request(
|
||||||
%{
|
%{
|
||||||
"method" => "tools/call",
|
"method" => "tools/call",
|
||||||
@@ -1382,18 +1427,25 @@ defmodule AgentCoordinator.MCPServer do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp extract_agent_context(request, _from, _state) do
|
defp extract_agent_context(request, _from, _state) do
|
||||||
# Try to get agent_id from various sources
|
method = Map.get(request, "method")
|
||||||
cond do
|
|
||||||
# From request arguments
|
|
||||||
Map.get(request, "params", %{})
|
|
||||||
|> Map.get("arguments", %{})
|
|
||||||
|> Map.get("agent_id") ->
|
|
||||||
agent_id = request["params"]["arguments"]["agent_id"]
|
|
||||||
%{agent_id: agent_id}
|
|
||||||
|
|
||||||
# If no explicit agent_id, return error - agents must register first
|
# For system calls, don't require agent_id
|
||||||
true ->
|
if method in ["initialize", "tools/list", "notifications/initialized"] do
|
||||||
{:error, "Missing agent_id. Agents must register themselves using register_agent before calling other tools."}
|
%{agent_id: nil} # System call, no agent context needed
|
||||||
|
else
|
||||||
|
# Try to get agent_id from various sources for non-system calls
|
||||||
|
cond do
|
||||||
|
# From request arguments
|
||||||
|
Map.get(request, "params", %{})
|
||||||
|
|> Map.get("arguments", %{})
|
||||||
|
|> Map.get("agent_id") ->
|
||||||
|
agent_id = request["params"]["arguments"]["agent_id"]
|
||||||
|
%{agent_id: agent_id}
|
||||||
|
|
||||||
|
# If no explicit agent_id, return error - agents must register first
|
||||||
|
true ->
|
||||||
|
{:error, "Missing agent_id. Agents must register themselves using register_agent before calling other tools."}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -340,9 +340,6 @@ defmodule AgentCoordinator.TaskRegistry do
|
|||||||
# Remove from pending since it was assigned
|
# Remove from pending since it was assigned
|
||||||
final_state = %{final_state | pending_tasks: state.pending_tasks}
|
final_state = %{final_state | pending_tasks: state.pending_tasks}
|
||||||
{:reply, {:ok, task}, final_state}
|
{:reply, {:ok, task}, final_state}
|
||||||
|
|
||||||
error ->
|
|
||||||
error
|
|
||||||
end
|
end
|
||||||
|
|
||||||
_conflicts ->
|
_conflicts ->
|
||||||
@@ -688,8 +685,6 @@ defmodule AgentCoordinator.TaskRegistry do
|
|||||||
:ok -> :ok
|
:ok -> :ok
|
||||||
# Inbox already stopped
|
# Inbox already stopped
|
||||||
{:error, :not_found} -> :ok
|
{:error, :not_found} -> :ok
|
||||||
# Continue regardless
|
|
||||||
_ -> :ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Publish unregistration event
|
# Publish unregistration event
|
||||||
|
|||||||
79
test_vscode_init.exs
Normal file
79
test_vscode_init.exs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env elixir
|
||||||
|
|
||||||
|
# Test script to simulate VS Code MCP initialization sequence
|
||||||
|
|
||||||
|
# Start the application
|
||||||
|
Application.start(:agent_coordinator)
|
||||||
|
|
||||||
|
# Wait a moment for the server to fully start
|
||||||
|
Process.sleep(1000)
|
||||||
|
|
||||||
|
# Test 1: Initialize call (system call, should work without agent_id)
|
||||||
|
IO.puts("Testing initialize call...")
|
||||||
|
init_request = %{
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => 1,
|
||||||
|
"method" => "initialize",
|
||||||
|
"params" => %{
|
||||||
|
"protocolVersion" => "2024-11-05",
|
||||||
|
"capabilities" => %{
|
||||||
|
"tools" => %{}
|
||||||
|
},
|
||||||
|
"clientInfo" => %{
|
||||||
|
"name" => "vscode",
|
||||||
|
"version" => "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_response = GenServer.call(AgentCoordinator.MCPServer, {:mcp_request, init_request})
|
||||||
|
IO.puts("Initialize response: #{inspect(init_response)}")
|
||||||
|
|
||||||
|
# Test 2: Tools/list call (system call, should work without agent_id)
|
||||||
|
IO.puts("\nTesting tools/list call...")
|
||||||
|
tools_request = %{
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => 2,
|
||||||
|
"method" => "tools/list"
|
||||||
|
}
|
||||||
|
|
||||||
|
tools_response = GenServer.call(AgentCoordinator.MCPServer, {:mcp_request, tools_request})
|
||||||
|
IO.puts("Tools/list response: #{inspect(tools_response)}")
|
||||||
|
|
||||||
|
# Test 3: Register agent call (should work)
|
||||||
|
IO.puts("\nTesting register_agent call...")
|
||||||
|
register_request = %{
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => 3,
|
||||||
|
"method" => "tools/call",
|
||||||
|
"params" => %{
|
||||||
|
"name" => "register_agent",
|
||||||
|
"arguments" => %{
|
||||||
|
"name" => "GitHub Copilot Test Agent",
|
||||||
|
"capabilities" => ["file_operations", "code_generation"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_response = GenServer.call(AgentCoordinator.MCPServer, {:mcp_request, register_request})
|
||||||
|
IO.puts("Register agent response: #{inspect(register_response)}")
|
||||||
|
|
||||||
|
# Test 4: Try a call that requires agent_id (should fail without agent_id)
|
||||||
|
IO.puts("\nTesting call that requires agent_id (should fail)...")
|
||||||
|
task_request = %{
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => 4,
|
||||||
|
"method" => "tools/call",
|
||||||
|
"params" => %{
|
||||||
|
"name" => "create_task",
|
||||||
|
"arguments" => %{
|
||||||
|
"title" => "Test task",
|
||||||
|
"description" => "This should fail without agent_id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task_response = GenServer.call(AgentCoordinator.MCPServer, {:mcp_request, task_request})
|
||||||
|
IO.puts("Task creation response: #{inspect(task_response)}")
|
||||||
|
|
||||||
|
IO.puts("\n✅ All tests completed!")"
|
||||||
Reference in New Issue
Block a user