lib/arke_ai.ex

defmodule ArkeAi do
  @moduledoc """
  AI integrations for [Arke](https://hex.pm/packages/arke). Currently exposes the
  `ArkeAi.Mcp.*` namespace for serving an Arke project to MCP-capable agents
  (Claude Desktop, Cursor, IDE integrations) over HTTP / Streamable HTTP.

  ## Installation

  Add `arke_ai` to your `mix.exs`:

      def deps do
        [
          {:arke_ai, "~> 0.1"}
        ]
      end

  ## Mounting in Phoenix

  Mount `ArkeAi.Mcp.Router` as a forward in your Phoenix router:

      defmodule MyAppWeb.Router do
        use MyAppWeb, :router

        forward "/mcp", ArkeAi.Mcp.Router,
          auth: :local,
          project: :my_project,
          otp_app: :my_app,
          expose: [
            {:arke,   :car,       [:list, :get, :search, :create, :update, :delete]},
            {:group,  :vehicle,   [:list, :get, :search, :list_arkes, :get_schema, :add_arke, :remove_arke]},
            {:system, :arke,      [:list, :get, :create, :update, :delete, :add_parameter, :remove_parameter]},
            {:system, :parameter, [:list, :get, :create, :update, :delete]},
            {:system, :group,     [:list, :get, :create, :update, :delete]}
          ]
      end

  ## Configuration

  ### `auth` — required

    * `:local` — IP-restricted to localhost. Trusted dev environment, no per-call permission checks.
      Project resolved from the `arke-project-key` request header, falling back to the `:project` opt.
    * `:bearer` — Guardian JWT verification via `ArkeAuth.Guardian`. Project and member extracted
      from the token. Per-call permission checks via `ArkeAuth.Utils.Permission` enforce op gates
      and row-level filter scoping.

  ### `expose` — required

  A list of `{type, id, ops}` tuples declaring which arkes / groups / system operations are visible
  to the agent. Each `ops` list is validated against the type's allowed ops.

  | Type | Valid ops |
  |---|---|
  | `:arke` | `:list`, `:get`, `:search`, `:create`, `:update`, `:delete` |
  | `:group` | `:list`, `:get`, `:search`, `:list_arkes`, `:get_schema`, `:add_arke`, `:remove_arke` |
  | `{:system, :arke}` | `:list`, `:get`, `:create`, `:update`, `:delete`, `:add_parameter`, `:remove_parameter` |
  | `{:system, :parameter}` | `:list`, `:get`, `:create`, `:update`, `:delete` |
  | `{:system, :group}` | `:list`, `:get`, `:create`, `:update`, `:delete` |

  System ops default to deny — only `super_admin` members can access them unless permission link
  units are explicitly seeded for other roles.

  ### `otp_app` — optional

  Used to derive `serverInfo.name` and `serverInfo.version` returned during `initialize`. If absent,
  defaults to `"arke_ai"` and `"0.1.0"`. Override individually with `:name` and `:version`.

  ### `project` — required for `:local`

  An atom matching an existing `:arke_project` id in `:arke_system`. Ignored for `:bearer` (project
  comes from the JWT).

  ## Transport

  `ArkeAi.Mcp.Router` implements MCP's [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):

    * `POST /mcp` — JSON-RPC request/response
    * `GET /mcp` with `Accept: text/event-stream` — Server-Sent Events stream for `notifications/tools/list_changed`
    * `mcp-session-id` response header echoed by the client

  Server pushes `notifications/tools/list_changed` after schema mutations
  (`:add_parameter`, `:create_*_parameter`, `:create_arke`, etc.) so connected agents refresh
  their cached tool lists.

  Stdio is not supported directly — bridge via [`mcp-remote`](https://www.npmjs.com/package/mcp-remote)
  for clients that only speak stdio.
  """
end