chore: added more @spec

This commit is contained in:
2026-05-01 17:49:50 +02:00
parent abcae1dad7
commit 881056eb61
157 changed files with 6223 additions and 1647 deletions

View File

@@ -30,7 +30,9 @@ defmodule BDS.MCP.AgentConfig do
end
def config_path(:claude_code, home_dir), do: Path.join(home_dir, ".claude.json")
def config_path(:github_copilot, home_dir), do: Path.join([home_dir, "Library", "Application Support", "Code", "User", "mcp.json"])
def config_path(:github_copilot, home_dir),
do: Path.join([home_dir, "Library", "Application Support", "Code", "User", "mcp.json"])
def packaged_executable_path(install_root, platform) when is_binary(install_root) do
executable_name =
@@ -90,12 +92,21 @@ defmodule BDS.MCP.AgentConfig do
defp merge_config(:github_copilot, config, command, args) do
servers = Map.get(config, "servers", %{})
Map.put(config, "servers", Map.put(servers, @server_name, %{"type" => "stdio", "command" => command, "args" => args}))
Map.put(
config,
"servers",
Map.put(servers, @server_name, %{"type" => "stdio", "command" => command, "args" => args})
)
end
defp merge_config(:claude_code, config, command, args) do
servers = Map.get(config, "mcpServers", %{})
Map.put(config, "mcpServers", Map.put(servers, @server_name, %{"command" => command, "args" => args}))
Map.put(
config,
"mcpServers",
Map.put(servers, @server_name, %{"command" => command, "args" => args})
)
end
defp remove_server_entry(:github_copilot, config) do

View File

@@ -8,7 +8,11 @@ defmodule BDS.MCP.Proposal do
schema "mcp_proposals" do
field :kind, :string
field :status, Ecto.Enum, values: [:pending, :accepted, :discarded, :expired], default: :pending
field :status, Ecto.Enum,
values: [:pending, :accepted, :discarded, :expired],
default: :pending
field :entity_id, :string
field :data, :map
field :created_at, :integer
@@ -17,7 +21,9 @@ defmodule BDS.MCP.Proposal do
def changeset(proposal, attrs) do
proposal
|> cast(attrs, [:id, :kind, :status, :entity_id, :data, :created_at, :expires_at], empty_values: [nil])
|> cast(attrs, [:id, :kind, :status, :entity_id, :data, :created_at, :expires_at],
empty_values: [nil]
)
|> validate_required([:id, :kind, :status, :entity_id, :data, :created_at, :expires_at])
|> unique_constraint(:status, name: :mcp_proposals_entity_idx)
end

View File

@@ -74,12 +74,15 @@ defmodule BDS.MCP.ProposalStore do
defp mark_status(id, status) do
case Repo.get(Proposal, id) do
nil -> nil
nil ->
nil
proposal ->
Repo.delete_all(
from other in Proposal,
where:
other.id != ^id and other.kind == ^proposal.kind and other.entity_id == ^proposal.entity_id and
other.id != ^id and other.kind == ^proposal.kind and
other.entity_id == ^proposal.entity_id and
other.status == ^status
)
@@ -90,6 +93,7 @@ defmodule BDS.MCP.ProposalStore do
end
defp derive_entity_id(data) do
data["post_id"] || data["script_id"] || data["template_id"] || data["media_id"] || Ecto.UUID.generate()
data["post_id"] || data["script_id"] || data["template_id"] || data["media_id"] ||
Ecto.UUID.generate()
end
end

View File

@@ -138,8 +138,11 @@ defmodule BDS.MCP.Server do
case URI.parse(target) do
%URI{path: "/mcp"} ->
case GenServer.call(__MODULE__, {:http_request, request}, 5_000) do
{:ok, status, body} -> http_response(status, Jason.encode!(body), "application/json", request.headers)
{:error, status, body} -> http_response(status, body, "text/plain", request.headers)
{:ok, status, body} ->
http_response(status, Jason.encode!(body), "application/json", request.headers)
{:error, status, body} ->
http_response(status, body, "text/plain", request.headers)
end
_other ->
@@ -170,7 +173,10 @@ defmodule BDS.MCP.Server do
success_response(id, %{
"protocolVersion" => Map.get(params, "protocolVersion", "2025-03-26"),
"capabilities" => %{"tools" => %{}, "resources" => %{}},
"serverInfo" => %{"name" => @server_name, "version" => Application.spec(:bds, :vsn) |> to_string()}
"serverInfo" => %{
"name" => @server_name,
"version" => Application.spec(:bds, :vsn) |> to_string()
}
})}
"tools/list" ->
@@ -196,10 +202,17 @@ defmodule BDS.MCP.Server do
arguments = Map.get(params, "arguments", %{})
case BDS.MCP.call_tool(name, arguments) do
{:ok, result} -> {:ok, success_response(id, %{"content" => [%{"type" => "json", "json" => result}]})}
{:error, :unknown_tool} -> {:error, error_response(id, -32601, "Unknown tool")}
{:error, :not_found} -> {:error, error_response(id, -32004, "Not found")}
{:error, reason} -> {:error, error_response(id, -32000, inspect(reason))}
{:ok, result} ->
{:ok, success_response(id, %{"content" => [%{"type" => "json", "json" => result}]})}
{:error, :unknown_tool} ->
{:error, error_response(id, -32601, "Unknown tool")}
{:error, :not_found} ->
{:error, error_response(id, -32004, "Not found")}
{:error, reason} ->
{:error, error_response(id, -32000, inspect(reason))}
end
end
@@ -286,7 +299,8 @@ defmodule BDS.MCP.Server do
|> IO.iodata_to_binary()
end
defp http_error_response(status, headers \\ %{}), do: http_response(status, reason_body(status), "text/plain", headers)
defp http_error_response(status, headers \\ %{}),
do: http_response(status, reason_body(status), "text/plain", headers)
defp reason_body(400), do: "Bad Request"
defp reason_body(404), do: "Not Found"

View File

@@ -9,8 +9,15 @@ defmodule BDS.MCP.Stdio do
if line != "" do
response =
case Jason.decode(line) do
{:ok, payload} -> handle_payload(payload)
{:error, _reason} -> %{"jsonrpc" => "2.0", "id" => nil, "error" => %{"code" => -32700, "message" => "Parse error"}}
{:ok, payload} ->
handle_payload(payload)
{:error, _reason} ->
%{
"jsonrpc" => "2.0",
"id" => nil,
"error" => %{"code" => -32700, "message" => "Parse error"}
}
end
IO.write(Jason.encode!(response) <> "\n")
@@ -18,14 +25,22 @@ defmodule BDS.MCP.Stdio do
end)
end
defp handle_payload(%{"jsonrpc" => "2.0", "id" => id, "method" => "initialize", "params" => params}) do
defp handle_payload(%{
"jsonrpc" => "2.0",
"id" => id,
"method" => "initialize",
"params" => params
}) do
%{
"jsonrpc" => "2.0",
"id" => id,
"result" => %{
"protocolVersion" => Map.get(params, "protocolVersion", "2025-03-26"),
"capabilities" => %{"tools" => %{}, "resources" => %{}},
"serverInfo" => %{"name" => "Blogging Desktop Server", "version" => Application.spec(:bds, :vsn) |> to_string()}
"serverInfo" => %{
"name" => "Blogging Desktop Server",
"version" => Application.spec(:bds, :vsn) |> to_string()
}
}
}
end
@@ -34,10 +49,26 @@ defmodule BDS.MCP.Stdio do
%{"jsonrpc" => "2.0", "id" => id, "result" => %{"tools" => BDS.MCP.list_tools()}}
end
defp handle_payload(%{"jsonrpc" => "2.0", "id" => id, "method" => "tools/call", "params" => %{"name" => name} = params}) do
defp handle_payload(%{
"jsonrpc" => "2.0",
"id" => id,
"method" => "tools/call",
"params" => %{"name" => name} = params
}) do
case BDS.MCP.call_tool(name, Map.get(params, "arguments", %{})) do
{:ok, result} -> %{"jsonrpc" => "2.0", "id" => id, "result" => %{"content" => [%{"type" => "json", "json" => result}]}}
{:error, reason} -> %{"jsonrpc" => "2.0", "id" => id, "error" => %{"code" => -32000, "message" => inspect(reason)}}
{:ok, result} ->
%{
"jsonrpc" => "2.0",
"id" => id,
"result" => %{"content" => [%{"type" => "json", "json" => result}]}
}
{:error, reason} ->
%{
"jsonrpc" => "2.0",
"id" => id,
"error" => %{"code" => -32000, "message" => inspect(reason)}
}
end
end
@@ -45,17 +76,38 @@ defmodule BDS.MCP.Stdio do
%{"jsonrpc" => "2.0", "id" => id, "result" => %{"resources" => BDS.MCP.list_resources()}}
end
defp handle_payload(%{"jsonrpc" => "2.0", "id" => id, "method" => "resources/read", "params" => %{"uri" => uri}}) do
defp handle_payload(%{
"jsonrpc" => "2.0",
"id" => id,
"method" => "resources/read",
"params" => %{"uri" => uri}
}) do
case BDS.MCP.read_resource(uri) do
{:ok, result} ->
%{"jsonrpc" => "2.0", "id" => id, "result" => %{"contents" => [%{"uri" => uri, "mimeType" => "application/json", "text" => Jason.encode!(result)}]}}
%{
"jsonrpc" => "2.0",
"id" => id,
"result" => %{
"contents" => [
%{"uri" => uri, "mimeType" => "application/json", "text" => Jason.encode!(result)}
]
}
}
{:error, reason} ->
%{"jsonrpc" => "2.0", "id" => id, "error" => %{"code" => -32000, "message" => inspect(reason)}}
%{
"jsonrpc" => "2.0",
"id" => id,
"error" => %{"code" => -32000, "message" => inspect(reason)}
}
end
end
defp handle_payload(%{"jsonrpc" => "2.0", "id" => id}) do
%{"jsonrpc" => "2.0", "id" => id, "error" => %{"code" => -32601, "message" => "Method not found"}}
%{
"jsonrpc" => "2.0",
"id" => id,
"error" => %{"code" => -32601, "message" => "Method not found"}
}
end
end

View File

@@ -60,8 +60,11 @@ defmodule BDS.MCP.Tools do
@spec validate_template(String.t()) :: {:ok, %{valid: boolean(), errors: [String.t()]}}
def validate_template(source) when is_binary(source) do
case Liquex.parse(source) do
{:ok, _ast} -> {:ok, %{valid: true, errors: []}}
{:error, reason, line} -> {:ok, %{valid: false, errors: ["#{inspect(reason)} at line #{line}"]}}
{:ok, _ast} ->
{:ok, %{valid: true, errors: []}}
{:error, reason, line} ->
{:ok, %{valid: false, errors: ["#{inspect(reason)} at line #{line}"]}}
end
end
@@ -276,7 +279,8 @@ defmodule BDS.MCP.Tools do
ttl_ms: @proposal_ttl_app_ms
)
{:ok, %{"proposal_id" => proposal.id, "current" => sanitize(media), "proposed" => changes}}
{:ok,
%{"proposal_id" => proposal.id, "current" => sanitize(media), "proposed" => changes}}
end
end