# SPDX-License-Identifier: Apache-2.0 # Copyright (c) 2025 Dashbit # See LICENSE for third-party notices. defmodule Corex.MCP.Router do @moduledoc false use Plug.Router import Plug.Conn require Logger alias Corex.Json alias Corex.MCP.Server @remote_access_forbidden """ For security reasons, Corex.MCP does not accept remote connections by default. If you really want to allow remote connections, configure plug Corex.MCP with allow_remote_access: true. """ @origin_header_forbidden """ For security reasons, Corex.MCP does not accept requests with an origin header for this endpoint. """ @json_parser Plug.Parsers.init( parsers: [:json], pass: [], json_decoder: Json.encoder() ) plug(:match) plug(:check_remote_ip) plug(:check_origin) plug(:dispatch) get "/" do conn |> put_resp_content_type("text/html") |> send_resp(200, landing_html()) |> halt() end get "/config" do conn |> put_resp_content_type("application/json") |> send_resp(200, Json.encode_to_iodata!(client_config())) |> halt() end get "/mcp" do conn |> put_logger_metadata() |> send_resp(405, "Method Not Allowed") |> halt() end post "/mcp" do conn |> put_logger_metadata() |> Plug.Parsers.call(@json_parser) |> Server.handle_http_message() |> halt() end match "/*_ignored" do conn |> put_logger_metadata() |> send_resp(404, "Not Found") |> halt() end defp check_remote_ip(conn, _opts) do config = conn.private.corex_mcp_config cond do local_address?(conn.remote_ip) -> conn config.allow_remote_access -> conn true -> Logger.warning(@remote_access_forbidden) conn |> send_resp(403, @remote_access_forbidden) |> halt() end end defp local_address?({127, 0, 0, _}), do: true defp local_address?({0, 0, 0, 0, 0, 0, 0, 1}), do: true defp local_address?({0, 0, 0, 0, 0, 65_535, 32_512, 1}), do: true defp local_address?(_), do: false defp check_origin(conn, _opts) do case {conn.path_info, get_req_header(conn, "origin")} do {[], _} -> conn {_, []} -> conn {_, _} -> Logger.warning(@origin_header_forbidden) conn |> send_resp(403, @origin_header_forbidden) |> halt() end end defp put_logger_metadata(conn) do Logger.metadata(corex_mcp: true) conn end defp landing_html do """ Corex MCP

Corex Model Context Protocol (dev). Use POST to /corex/mcp for JSON-RPC.

""" end defp client_config do %{ name: "corex", framework_type: "phoenix", corex_version: package_version(:corex) } end defp package_version(app) do case Application.spec(app, :vsn) do nil -> nil vsn -> List.to_string(vsn) end end end