# ConduitMCP
An Elixir implementation of the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) specification (version 2025-06-18). Build MCP servers to expose tools, resources, and prompts to LLM applications like Claude Desktop and VS Code extensions.
## Installation
Add to your `mix.exs`:
```elixir
def deps do
[
{:conduit_mcp, "~> 0.2.0"}
]
end
```
Or from GitHub:
```elixir
def deps do
[
{:conduit_mcp, github: "nyo16/conduit_mcp"}
]
end
```
## Quick Start
### Standalone Server
```elixir
defmodule MyApp.MCPServer do
use ConduitMcp.Server
@impl true
def mcp_init(_opts) do
tools = [
%{
"name" => "greet",
"description" => "Greet someone",
"inputSchema" => %{
"type" => "object",
"properties" => %{
"name" => %{"type" => "string"}
},
"required" => ["name"]
}
}
]
{:ok, %{tools: tools}}
end
@impl true
def handle_list_tools(state) do
{:reply, %{"tools" => state.tools}, state}
end
@impl true
def handle_call_tool("greet", %{"name" => name}, state) do
{:reply, %{
"content" => [%{"type" => "text", "text" => "Hello, #{name}!"}]
}, state}
end
end
```
Start the server:
```elixir
children = [
{MyApp.MCPServer, []},
{Bandit,
plug: {ConduitMcp.Transport.StreamableHTTP, server_module: MyApp.MCPServer},
port: 4001}
]
Supervisor.start_link(children, strategy: :one_for_one)
```
### Phoenix Integration
Add MCP endpoints directly to your Phoenix application:
```elixir
# lib/my_app/mcp_server.ex
defmodule MyApp.MCPServer do
use ConduitMcp.Server
@impl true
def mcp_init(_opts) do
tools = [
%{
"name" => "get_user",
"description" => "Get user information from database",
"inputSchema" => %{
"type" => "object",
"properties" => %{
"user_id" => %{"type" => "string"}
},
"required" => ["user_id"]
}
}
]
{:ok, %{tools: tools}}
end
@impl true
def handle_list_tools(state) do
{:reply, %{"tools" => state.tools}, state}
end
@impl true
def handle_call_tool("get_user", %{"user_id" => id}, state) do
user = MyApp.Accounts.get_user(id)
{:reply, %{
"content" => [%{"type" => "text", "text" => "User: #{user.name}"}]
}, state}
end
end
```
Add to your router:
```elixir
# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
use MyAppWeb, :router
pipeline :mcp do
# Optional: Add authentication
plug MyAppWeb.Plugs.MCPAuth, enabled: false
end
scope "/mcp", MyAppWeb do
pipe_through :mcp
forward "/", ConduitMcp.Transport.StreamableHTTP,
server_module: MyApp.MCPServer
end
end
```
Add server to your application supervision tree:
```elixir
# lib/my_app/application.ex
def start(_type, _args) do
children = [
MyApp.MCPServer,
MyAppWeb.Endpoint
]
Supervisor.start_link(children, strategy: :one_for_one)
end
```
See [examples/phoenix_mcp/](examples/phoenix_mcp/) for a complete working example with authentication.
## Client Configuration
### VS Code / Cursor
Add to your MCP settings:
```json
{
"mcpServers": {
"my-app": {
"url": "http://localhost:4001/"
}
}
}
```
### Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"my-app": {
"command": "elixir",
"args": ["/path/to/your/server.exs"]
}
}
}
```
## Features
- Full MCP specification 2025-06-18 implementation
- Dual transport support (Streamable HTTP and SSE)
- JSON-RPC 2.0 compliant
- Support for tools, resources, and prompts
- Configurable CORS and authentication
- Phoenix integration support
- Telemetry events for monitoring
- Production ready with comprehensive test coverage (82%)
## Testing
```bash
# Run tests
mix test
# Run with coverage
mix coveralls
# Test with curl
curl -X POST http://localhost:4001/ \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
## Documentation
- [Implementation Guide](elixir_mcp_implementation_guide.md)
- [Simple Server Example](examples/simple_tools_server/README.md)
- [Phoenix Integration Example](examples/phoenix_mcp/README.md)
- [MCP Specification](https://modelcontextprotocol.io/specification/)
## Requirements
- Elixir 1.18+
- Erlang/OTP 27+
## License
Apache License 2.0