# MCPKit

[![Hex.pm](https://img.shields.io/hexpm/v/mcp_kit.svg)](https://hex.pm/packages/mcp_kit)
[![CI](https://github.com/mcostasilva/mcp_kit/actions/workflows/ci.yml/badge.svg)](https://github.com/mcostasilva/mcp_kit/actions/workflows/ci.yml)
[![HexDocs](https://img.shields.io/badge/docs-hexdocs-blue.svg)](https://hexdocs.pm/mcp_kit)
[![License](https://img.shields.io/hexpm/l/mcp_kit.svg)](https://github.com/mcostasilva/mcp_kit/blob/master/LICENSE)

`MCPKit` is a reusable MCP server runtime for Phoenix applications.

It gives host applications a small set of host-facing contracts for building
policy-aware MCP servers over Streamable HTTP, with support for tools, prompts,
resources, completions, persistent sessions, and a host-started runtime for
active sessions.

## MCP Support Matrix

| Area | Status | Notes |
| --- | --- | --- |
| Streamable HTTP transport (`POST`) | ✅ | Supported |
| SSE session stream (`GET`) | ✅ | Supported for active sessions |
| Session termination (`DELETE`) | ✅ | Supported |
| Lifecycle (`initialize`, `ping`, `notifications/initialized`) | ✅ | Supported |
| Tools (`tools/list`, `tools/call`) | ✅ | Supported |
| Prompts (`prompts/list`, `prompts/get`) | ✅ | Supported |
| Resources (`resources/list`, `resources/read`, `resources/templates/list`) | ✅ | Supported |
| Utilities (`completion/complete`, `notifications/cancelled`) | ✅ | Supported |
| Request-time policy enforcement | ✅ | Via `MCPKit.Policy` |
| Resource subscriptions | ❌ | Not implemented |
| Resource update notifications | ❌ | Not implemented |
| Logging capability | ❌ | Not implemented |
| Progress notifications | ❌ | Not implemented |
| List-changed notifications | ❌ | Not implemented |
| Public host API for runtime-initiated client requests | ❌ | Internal plumbing exists, but no public host API yet |

## Installation

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

## Quickstart

### 1. Define the host contract

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

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

  @impl true
  def session_store, do: MyApp.MCP.SessionStore

  @impl true
  def policy, do: MyApp.MCP.Policy
end
```

`MCPKit` owns the MCP transport version and advertises its built-in current
protocol version automatically. Host applications do not configure this.

### 2. Implement persistent session storage

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

  @impl true
  def create_session(attrs) do
    {:ok,
     %{
       id: Ecto.UUID.generate(),
       initialized: false,
       protocol_version: attrs.protocol_version,
       client_info: attrs.client_info,
       client_capabilities: attrs.client_capabilities
     }}
  end

  @impl true
  def fetch_session(session_id) do
    # Load from your database or cache.
    {:error, :not_found}
  end

  @impl true
  def touch_session(session), do: {:ok, session}

  @impl true
  def mark_initialized(session), do: {:ok, %{session | initialized: true}}

  @impl true
  def delete_session(_session_id), do: {:error, :not_found}
end
```

### 3. Start the runtime

```elixir
children = [
  {MCPKit.Runtime, definition: MyApp.MCP.Definition}
]
```

### 4. Add a policy

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

  @impl true
  def authorize(_action, _context), do: :allow
end
```

### 5. Define a tool

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

  alias MCPKit.Response

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

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

### 6. Mount the MCP route

```elixir
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  import MCPKit.Router

  mcp_scope "/mcp", MyApp.MCP do
    tool "ping", Tools.Ping
  end
end
```

`mcp_scope/2` infers the definition module as `MyApp.MCP.Definition` and the
runtime name as `MyApp.MCP.Runtime`. Override them with `definition:` and
`runtime:` if needed.

Inside `mcp_scope`, module references follow normal Phoenix scope aliasing, so
`Tools.Ping` resolves as `MyApp.MCP.Tools.Ping` and remains visible to editor
tooling.

## Host Contracts

The host application is responsible for:

- `MCPKit.Definition`: protocol version, server info, session store, and optional policy
- `MCPKit.SessionStore`: durable session lifecycle
- `MCPKit.Runtime`: active in-memory session coordination
- `MCPKit.Policy`: request-time authorization and visibility decisions

See the guides for fuller examples:

- [Getting Started](guides/getting-started.md)
- [Policy and Tiered Access](guides/policy.md)
- [Supported MCP Surface](guides/supported-mcp-surface.md)

## Router DSL

Use `MCPKit.Router.mcp_scope/2` or `mcp_scope/3` inside a Phoenix router scope.

```elixir
mcp_scope "/mcp", MyApp.MCP do
  tool "project_create", Tools.ProjectCreate
  prompt "draft_release_notes", Prompts.DraftReleaseNotes
  resource "project", Resources.Project
end
```

Inside `mcp_scope`, you can declare:

- `tool/2`
- `prompt/2`
- `resource/2`

## Policy

`MCPKit.Policy` is evaluated on every request. It supports these decisions:

- `:allow`
- `{:deny, :not_found}`
- `{:deny, :forbidden}`

Lists are filtered item-by-item, while direct calls, prompt gets, resource reads,
and completions are enforced separately.

`completion/complete` is also policy-aware and only returns suggestions for
visible prompt arguments and resource templates.

## Authoring APIs

### Tools

Tool modules use `MCPKit.Tool` to declare input schemas and return
`MCPKit.Response` values.

### Prompts

Prompt modules use `MCPKit.Prompt` to declare argument schemas, render MCP
messages, and optionally implement `complete/3` for argument suggestions.

### Resources

Resource modules use `MCPKit.Resource` to declare concrete resource entries,
URI templates, URI reads, and optionally implement `complete/3` for template
variable suggestions.

## Supported MCP Surface

Currently implemented methods:

- `initialize`
- `ping`
- `completion/complete`
- `prompts/list`
- `prompts/get`
- `resources/list`
- `resources/read`
- `resources/templates/list`
- `tools/list`
- `tools/call`
- `notifications/initialized`
- `notifications/cancelled`
- `DELETE` session termination
- `GET` SSE session stream

For one canonical compatibility summary, see
[Supported MCP Surface](guides/supported-mcp-surface.md).

## Publishing Checklist

- GitHub Actions publishes tagged releases automatically from `v*` tags
- configure the `HEX_API_KEY` repository secret before pushing a release tag
- update version in `mix.exs`
- confirm package links and source URL
- run `mix test`
- run `mix docs`
- run `mix hex.build`
