@tyr0 one major suggestion, something I’d ideally need to make AshAI use this, is to make it like plug which accepts opts, and all functions accept those options as their first argument, post initialization.
In practice, it looks like this:
defmodule MyApp.Tools.CalculateSum do
use Vancouver.Tool
# imagine (for whatever reason) you wanted a "fast" and a "slow" version of `sum`
def init(opts) do
# validate opts
{:ok, %{speed: opts[:speed]}}
end
def name(%{speed: :slow}), do: "calculate_sum"
def name(%{speed: :fast}), do: "calculate_sum_quickly"
def description(opts) do
description = "Add two numbers together"
if opts.speed == :fast do
"""
#{description}
This costs extra money, only use when you need to
add many numbers together quickly.
"""
else
description
end
end
def input_schema(_) do
%{
"type" => "object",
"properties" => %{
"a" => %{"type" => "number"},
"b" => %{"type" => "number"}
},
"required" => ["a", "b"]
}
end
def run(conn, opts, %{"a" => a, "b" => b}) do
if opts.speed == :fast do
send_text(conn, "#{sum_fast(a, b)}")
else
send_text(conn, "#{sum_slow(a, b)}")
end
end
end
and then in the router:
forward "/mcp", Vancouver.Router, tools: [
{MyApp.Tools.CalculateSum, speed: :fast},
{MyApp.Tools.CalculationSum, speed: :slow}
]
And finally, add a concept of extensions that can dynamically add tools.
defmodule SomeExtensions do
use Vancouver.Extension
def add_tools(opts) do
[...new_tools]
end
end
which would then look like this:
forward "/mcp", Vancouver.Router, tools: [
{MyApp.Tools.CalculateSum, speed: :fast},
{MyApp.Tools.CalculationSum, speed: :slow}
], extensions: [{AshAi.Mcp, otp_app: :otp_app}]
This allows layers to be built dynamically on top of Vancouver. If that happens then I’ll be able to consider using it as a basis for AshAI’s MCP tooling (which is not something I’m saying you should care about
, just letting you know what would make it work for my use case).






















