README.md

# McpKit

`McpKit` provides a small, reusable MCP runtime for Phoenix applications.

It is designed for applications that want:
- a tools-first MCP server over Streamable HTTP
- persistent session storage owned by the host app
- a Phoenix router DSL that hides MCP transport boilerplate

Current scope:
- Streamable HTTP endpoint with tools support
- JSON-RPC protocol helpers
- tool behavior and input schema DSL
- Phoenix router DSL with `mcp_scope`

Not implemented yet:
- prompts
- resources
- SSE `GET /mcp`

## Installation

Hex:

```elixir
def deps do
  [
    {:mcp_kit, "~> 0.1.0"}
  ]
end
```

## Local Usage

```elixir
def deps do
  [
    {:mcp_kit, path: "../mcp_kit"}
  ]
end
```

## Host Contracts

The host application provides two things:
- a definition module implementing `MCPKit.Definition`
- a persistent session store implementing `MCPKit.SessionStore`

Example:

```elixir
defmodule MyApp.MCP.Definition do
  @behaviour MCPKit.Definition

  def protocol_version, do: "2025-06-18"

  def server_info do
    %{"name" => "my-app", "version" => "0.1.0"}
  end

  def session_store, do: MyApp.MCP.SessionStore
end
```

## Router Usage

```elixir
mcp_scope "/mcp", MyApp.MCP do
  tool "project_create", Tools.ProjectCreate
  tool "project_status", Tools.ProjectStatus
end
```

`mcp_scope` infers the host definition module as `MyApp.MCP.Definition` unless
you override it with `definition:`.

`prompt/2` and `resource/2` are intentionally reserved in the DSL and currently
raise compile-time errors until those runtime surfaces are implemented.

## Supported MCP Surface

Current supported methods:
- `initialize`
- `notifications/initialized`
- `ping`
- `tools/list`
- `tools/call`
- `notifications/cancelled`
- `DELETE` session termination

Current transport limitations:
- `GET` returns `405`
- SSE transport is not implemented yet

## Tool Modules

Tool modules use `MCPKit.Tool` and return `MCPKit.Response` values:

```elixir
defmodule MyApp.MCP.Tools.Ping do
  use MCPKit.Tool

  alias MCPKit.Response

  schema do
    field :message, :string, required: true
  end

  def execute(arguments, context) do
    {:reply, Response.tool() |> Response.structured(arguments), context}
  end
end
```

## Publish Checklist

- update version in `mix.exs`
- confirm package links and source URL
- run `mix test`
- run `mix docs`
- run `mix hex.build`