{erl_opts, [debug_info, warnings_as_errors]}.
{deps, [
%% hex package is `erlang_h1' (the short name `h1' was taken);
%% the OTP app/dep stays `h1' so call sites and the app graph
%% use `h1' consistently.
{h1, "~> 0.7.0", {pkg, erlang_h1}},
{h2, "~> 0.10.0"},
{hackney, "~> 4.4.2"}
]}.
{shell, [{apps, [barrel_mcp]}]}.
{eunit_opts, [verbose]}.
{cover_enabled, true}.
{dialyzer, [
{warnings, [unmatched_returns, error_handling, unknown]},
{plt_extra_apps, [ssl, public_key]}
]}.
{xref_checks, [
undefined_function_calls,
undefined_functions,
locals_not_used,
deprecated_function_calls,
deprecated_functions
]}.
{profiles, [
{test, [
{deps, [{meck, "1.2.0"}]}
]}
]}.
%% meck 1.2.0 still uses the bare `catch' operator, which OTP 29
%% deprecates; relax that one warning for the dep so it builds (our
%% own code uses try...catch).
{overrides, [
{add, meck, [{erl_opts, [nowarn_deprecated_catch]}]}
]}.
%% Documentation
{project_plugins, [
{erlfmt, "1.8.0"},
{rebar3_lint, "5.0.4"},
rebar3_ex_doc
]}.
{erlfmt, [
write,
{files, [
"{src,include,test}/**/*.{hrl,erl,app.src}",
"rebar.config"
]}
]}.
{ex_doc, [
{extras, [
{"README.md", #{title => "Overview"}},
{"guides/getting-started.md", #{title => "Getting Started"}},
{"guides/building-a-client.md", #{title => "Building a Client"}},
{"guides/internals.md", #{title => "Client Internals"}},
{"guides/features.md", #{title => "Features"}},
{"guides/http-stream.md", #{title => "Streamable HTTP Transport"}},
{"guides/stdio.md", #{title => "stdio Transport"}},
{"guides/authentication.md", #{title => "Authentication"}},
{"guides/custom-authentication.md", #{title => "Custom Authentication"}},
{"guides/tools-resources-prompts.md", #{title => "Tools, Resources & Prompts"}},
{"guides/client.md", #{title => "MCP Client (reference)"}},
{"docs/pending-features.md", #{title => "Pending Features"}},
{"CHANGELOG.md", #{title => "Changelog"}},
{"LICENSE", #{title => "License"}}
]},
{main, "README.md"},
{homepage_url, "https://github.com/barrel-platform/barrel_mcp"},
{source_url, "https://github.com/barrel-platform/barrel_mcp"},
{api_reference, true},
{groups_for_modules, [
{"Main API", [barrel_mcp]},
{"Server Authentication", [
barrel_mcp_auth,
barrel_mcp_auth_none,
barrel_mcp_auth_bearer,
barrel_mcp_auth_apikey,
barrel_mcp_auth_basic,
barrel_mcp_auth_custom
]},
{"Server", [
barrel_mcp_http,
barrel_mcp_http_stream,
barrel_mcp_http_engine,
barrel_mcp_http_listener,
barrel_mcp_stdio,
barrel_mcp_protocol
]},
{"Session", [barrel_mcp_session]},
{"Client", [
barrel_mcp_client,
barrel_mcp_clients,
barrel_mcp_client_handler,
barrel_mcp_client_handler_default,
barrel_mcp_client_transport,
barrel_mcp_client_stdio,
barrel_mcp_client_http
]},
{"Client Authentication", [
barrel_mcp_client_auth,
barrel_mcp_client_auth_bearer,
barrel_mcp_client_auth_oauth
]},
{"Utilities", [barrel_mcp_pagination, barrel_mcp_schema]},
{"Registry", [barrel_mcp_registry]}
]}
]}.
{hex, [{doc, ex_doc}]}.
{elvis, [
{config, [
#{
files => ["src/**/*.erl"],
rules => [
{elvis_style, no_macros, disable},
{elvis_style, no_common_caveats_call, disable},
{elvis_style, no_init_lists, disable},
{elvis_style, param_pattern_matching, disable},
%% `barrel_mcp' is the intentional public facade; the
%% client/registry/engine/session modules expose broad
%% surfaces (verbs, dispatch, state machine) by design.
{elvis_style, no_god_modules, #{
ignore => [
barrel_mcp,
barrel_mcp_client,
barrel_mcp_registry,
barrel_mcp_http_engine,
barrel_mcp_session
]
}},
%% Auth/transport/handler providers are dispatched
%% dynamically by design (Provider:init/verify,
%% Transport:send/recv, Handler:handle_request, ...).
{elvis_style, no_invalid_dynamic_calls, #{
ignore => [
barrel_mcp_auth,
barrel_mcp_auth_custom,
barrel_mcp_client,
barrel_mcp_client_auth,
barrel_mcp_client_transport,
barrel_mcp_registry,
barrel_mcp_http_engine,
barrel_mcp_http_listener,
barrel_mcp_protocol
]
}},
%% Transport/session loops block on receive and exit on a
%% monitored DOWN, so an after clause is wrong.
{elvis_style, no_receive_without_timeout, #{
ignore => [
barrel_mcp_http_engine,
barrel_mcp_http_listener,
barrel_mcp_client_http,
barrel_mcp_client_stdio,
barrel_mcp_stdio,
barrel_mcp_session
]
}},
%% camelCase JSON-RPC / JSON-Schema keyword atoms are
%% external wire shapes, not our naming.
{elvis_style, atom_naming_convention, #{
ignore => [
barrel_mcp_protocol,
barrel_mcp_schema,
barrel_mcp_tool_format
]
}},
%% The DRY heuristic flags structurally similar but
%% intentionally distinct JSON-RPC / schema / state /
%% transport branches throughout; off project-wide.
{elvis_style, dont_repeat_yourself, disable},
%% Internal opaque token/handle types kept private to the
%% OAuth client module.
{elvis_style, private_data_types, #{
ignore => [barrel_mcp_client_auth_oauth]
}},
{elvis_style, no_boolean_in_comparison, #{
ignore => [barrel_mcp_protocol]
}},
{elvis_style, no_single_clause_case, #{
ignore => [barrel_mcp_client]
}},
%% stdio transport writes the framed protocol response to
%% stdout; that io:format is the wire, not a debug call.
{elvis_style, no_debug_call, #{
ignore => [barrel_mcp_stdio]
}},
%% The acceptor/handler plumbing nests and threads the
%% per-stream handler fun positionally.
{elvis_style, no_deep_nesting, #{
ignore => [barrel_mcp_http_listener]
}},
{elvis_style, max_anonymous_function_arity, #{
ignore => [barrel_mcp_http_listener]
}},
%% Long lines flagged here are doc-comment URLs and edoc
%% examples that cannot wrap; code lines stay under 100.
{elvis_text_style, max_line_length, #{
limit => 100,
skip_comments => whole_line
}}
],
ruleset => erl_files
},
#{
files => ["rebar.config"],
rules => [
{elvis_project, no_branch_deps, disable}
],
ruleset => rebar_config
}
]}
]}.