tyr0

tyr0

Vancouver - easily add MCP tools to your phoenix/bandit server

Vancouver makes it easy to add Model Context Protocol (MCP) functionality to your Phoenix/Bandit server. Vancouver handles initialization, request validation, and offers helper functions to simplify the creation of MCP tools and prompts.

The goal is to let you create MCP servers with working tools/prompts in minutes, without needing to know much about the MCP protocol.

How it works

You create tools like this:

defmodule MyApp.Tools.CalculateSum do
  use Vancouver.Tool

  def name, do: "calculate_sum"
  def description, do: "Add two numbers together"

  def input_schema do
    %{
      "type" => "object",
      "properties" => %{
        "a" => %{"type" => "number"},
        "b" => %{"type" => "number"}
      },
      "required" => ["a", "b"]
    }
  end

  def run(conn, %{"a" => a, "b" => b}) do
    send_text(conn, "#{a + b}")
  end
end

create prompts like this:

defmodule MyApp.Prompts.CodeReview do
  use Vancouver.Prompt

  def name, do: "code_review"
  def description, do: "Asks the LLM to analyze code quality and suggest improvements"

  def arguments do
    [
      %{
        "name" => "code",
        "description" => "The code to review",
        "required" => true
      }
    ]
  end

  def run(conn, %{"code" => code}) do
    send_text(conn, "Please review this code: #{code}")
  end
end

and configure via your (phoenix) router like this:

forward "/mcp", Vancouver.Router, 
  tools: [MyApp.Tools.CalculateSum],
  prompts: [MyApp.Prompts.CodeReview]

To use your tools locally with e.g. Claude Desktop, you can update your claude_desktop_config.json file (see below), run your server, and refresh Claude Desktop.

{
    "mcpServers": {
        "MyApp": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "http://localhost:4000/mcp"
            ]
        }
    }
}

More info

Most Liked

zachdaniel

zachdaniel

Creator of Ash

We’ve been looking for a generic MCP solution to sit Ash AI on top of that can act as a shared base instead of rolling our own which is what we’ve done so far. I’ll be evaluating this for that purpose.

mtrudel

mtrudel

Creator of Bandit

Very nice! I’ve also started GitHub - mtrudel/excom: EXCOM is an MCP server for Elixir and it looks like our config ergonomics are more or less identical. TBH, EXCOM came out of a company hack week and I don’t really have the bandwidth to keep carrying it along, happy to see some other projects emerging to fill that need!

venkatd

venkatd

I’m a huge fan of this API design–simple but extensible. My vote goes for something like this!

One thing that might be helpful is to allow some way to manage session state, more akin to a phoenix channel.

For example, I am prototyping an MCP server for Livebook, and upon connecting the AI agent will register as a collaborator in the notebook. On disconnect, it will leave. This means some MCP servers may need to be stateful.

I don’t know if this is within the scope but wanted to share that this comes up pretty often.

tyr0

tyr0

Aha. I was planning to make Tools (and Prompts) into plugs, so that they’d have an (overridable) init, and support pipelines etc. I think that should work for your use case too.

Will have a think about extension support - that’s not something I’d considered, but sounds interesting. :slightly_smiling_face:

dbern

dbern

Love it! Very nice and simple API for defining tools. I know this is limited to tools right now, but do you plan to support prompts or resources?

I’m working on a framework that provides a controller-like DSL that can respond synchronously and asynchronously: GitHub - dbernheisel/phantom_mcp: MCP server implemented in Elixir

I would love to collaborate. I’m in the process of using Phantom in anger before I publicize it.

Where Next?

Popular in Libraries Top

josevalim
Hi everyone, We would like to announce that Plataformatec is working on a new MySQL driver called MyXQL. Our goal is to eventually integ...
New
zoltanszogyenyi
Hey everyone :wave: Excited to join this forum - I am one of the founders and current project maintainers of a popular and open-source U...
New
tompave
Hello there, I would like to share a feature toggles library (AKA feature flags) I’ve been working on. The main package is FunWithFlags...
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
michalmuskala
Another small library today. PersistentEts Hex: persistent_ets | Hex GitHub: GitHub - michalmuskala/persistent_ets Ets table backed by...
New
danschultzer
In short Plug n’ play OAuth 2.0 provider library. Just set up a resource owner schema with Ecto (your user schema), install the dependen...
New
wfgilman
I’ve cleaned up and open sourced three financial libraries I was using for my company. They are bindings for the APIs of these three comp...
New
tfwright
After working on it for a couple of months and using it in production for most of that time, today I’ve released LiveAdmin, a LiveView ba...
New
Crowdhailer
I have been updating a library that allows you to pipe between functions that use the erlang result tuple convention. Assuming you have...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
381 12391 119
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

Sub Categories:

We're in Beta

About us Mission Statement