streamable-httpupdated 3mo ago
A streamable HTTP Model Context Protocol server that exposes BMLT (Basic Meeting List Toolbox ā the Narcotics Anonymous meeting directory) as a set of read-only tools that AI assistants can call directly.
What can you do with BMLT Meetings?
bmlt-server-mcp
A streamable HTTP Model Context Protocol server that exposes BMLT (Basic Meeting List Toolbox ā the Narcotics Anonymous meeting directory) as a set of read-only tools that AI assistants can call directly.
BMLT hosts approximately 85% of Narcotics Anonymous meetings worldwide; the default backend is the BMLT aggregator, which federates every public BMLT root server (server list) into a single search surface ā effectively the authoritative AI-accessible source for finding NA meetings.
MCP is an open protocol, so any compatible client works: Claude (Code, Desktop, web), ChatGPT (Connectors and the Responses API), Google Gemini, Cursor, Windsurf, Zed, Cline, Continue, and others.
Unlike the npm bmlt-mcp-server (stdio-only, runs as a local subprocess), this server speaks MCP over HTTP and is meant to be hosted once and consumed remotely.
Built on Laravel + PHP 8.2+ using the official laravel/mcp package.
A live deployment runs at https://mcp.bmlt.app/ (landing page) with a tools reference at https://mcp.bmlt.app/reference.
Tools
All tools are read-only and idempotent. All tools accept an optional root_server_url argument; if omitted, the configured BMLT_ROOT_SERVER_URL is used.
| Tool | What it does |
|---|---|
search_meetings |
Search meetings by address (geocoded server-side) or lat/lng + radius, filtered by weekday, time, format, venue type (in-person / virtual / hybrid), service body, and free text. Returns a curated summary by default (data_format=full for the raw BMLT response). |
get_meeting |
Fetch a single meeting by its BMLT id_bigint. |
list_formats |
List meeting format codes (Open, Closed, Speaker, Beginners, language tags, ā¦) so callers can map names ā IDs for search_meetings. |
list_service_bodies |
List zones / regions / areas / groups so callers can map names ā IDs for search_meetings. |
get_server_info |
Capabilities, version, languages, and default coordinates for the configured root server. |
list_root_servers |
Public BMLT root servers known to the aggregator. Useful when the caller wants to switch roots. |
Quick start
Docker (recommended)
cp .env.example .env
docker compose up --build
The MCP endpoint is then live at http://localhost:8080/mcp over the Streamable HTTP transport.
Local PHP
composer install
cp .env.example .env
php artisan key:generate
php artisan serve # http://localhost:8000/mcp
Verify with the MCP Inspector
php artisan mcp:inspector
Then connect to http://localhost:8000/mcp (or :8080 for Docker) and list tools.
Configuration
All configuration lives in .env (see .env.example). The interesting bits:
| Variable | Purpose |
|---|---|
BMLT_ROOT_SERVER_URL |
Required. Default BMLT root server, including /main_server path. |
BMLT_ALLOWED_ROOTS |
Comma-separated allowlist for the optional root_server_url tool argument. The default root is always implicitly allowed. |
BMLT_ALLOW_ANY_ROOT |
true allows any URL via root_server_url ā not recommended in production (enables SSRF-style queries). Off by default. |
BMLT_AGGREGATOR_URL |
Aggregator queried by list_root_servers. Defaults to the public aggregator. |
GEOCODER |
nominatim (default), google, or null. null rejects address inputs and requires lat/lng. |
NOMINATIM_USER_AGENT |
Identifies your deployment to OSM ā required by Nominatim's ToS. Always set this in production. |
GOOGLE_GEOCODER_API_KEY |
Required when GEOCODER=google. |
Why an allowlist?
The optional root_server_url tool argument lets a single deployment serve any BMLT root, but accepting arbitrary URLs would let callers turn the server into an SSRF probe against your network. The default is a strict allowlist (the configured default root, plus anything you add to BMLT_ALLOWED_ROOTS). Set BMLT_ALLOW_ANY_ROOT=true only when the server is isolated from anything sensitive.
Connecting AI clients
The endpoint of a deployed instance is https://your-host.example.com/mcp. Replace it below with your own host (or use https://mcp.bmlt.app/mcp to try the public instance).
Claude Code (CLI)
claude mcp add --transport http bmlt https://your-host.example.com/mcp
Claude Desktop ā Custom Connector (newer builds)
Settings ā Connectors ā Add custom connector ā paste the URL.
Claude Desktop ā Config file (any version, needs Node.js)
{
"mcpServers": {
"bmlt": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://your-host.example.com/mcp"]
}
}
}
ChatGPT / OpenAI Responses API
{
"tools": [
{
"type": "mcp",
"server_label": "bmlt",
"server_url": "https://your-host.example.com/mcp"
}
]
}
In ChatGPT itself (Pro/Business/Enterprise): Settings ā Connectors ā Add, using the same URL.
Cursor / Windsurf / Zed / Cline / Continue
All of these read an mcpServers block. For Cursor, edit ~/.cursor/mcp.json (or the project-local .cursor/mcp.json); other clients use a similar config file.
{
"mcpServers": {
"bmlt": {
"url": "https://your-host.example.com/mcp"
}
}
}
For per-tool parameter documentation and BMLT-API mappings, see the live reference page. For day-to-day commands on a deployed instance (log tailing, usage summaries, cache clears, upgrades), see docs/operations.md.
Adding authentication
The default config has no auth ā anyone who can reach /mcp can call the tools. To gate it:
// routes/ai.php
use Laravel\Mcp\Facades\Mcp;
Mcp::oauthRoutes(); // OAuth 2.1 (Laravel Passport)
Mcp::web('/mcp', BmltServer::class)
->middleware('auth:api');
Or add a simple bearer-token check via custom middleware. See the Laravel MCP docs for the full options.
Architecture
app/
āāā Mcp/
ā āāā Servers/BmltServer.php # Registers the 6 tools
ā āāā Tools/
ā āāā SearchMeetingsTool.php
ā āāā GetMeetingTool.php
ā āāā ListFormatsTool.php
ā āāā ListServiceBodiesTool.php
ā āāā GetServerInfoTool.php
ā āāā ListRootServersTool.php
ā āāā Concerns/ResolvesBmltClient.php
āāā Services/
ā āāā Bmlt/
ā ā āāā BmltClient.php # Wraps client_interface/json
ā ā āāā BmltClientFactory.php # Allowlist enforcement
ā ā āāā BmltException.php
ā āāā Geocoding/
ā āāā Geocoder.php # Interface
ā āāā GeocoderManager.php # Driver resolver
ā āāā NominatimGeocoder.php # OSM (rate-limited, cached)
ā āāā GoogleGeocoder.php # Google Geocoding API
ā āāā NullGeocoder.php # Disabled
ā āāā GeocodingResult.php
ā āāā GeocodingException.php
āāā Providers/BmltServiceProvider.php
config/bmlt.php # All knobs
routes/ai.php # Mcp::web('/mcp', BmltServer::class)
The HTTP API wrapped by BmltClient is formally specified by the
BMLT Semantic OpenAPI document
(OpenAPI 3.1) ā refer to it for every parameter, response shape, and field definition
that BMLT itself supports, even if this MCP server doesn't yet expose it as a tool argument.
License
MIT
Install
Add BMLT Meetings to your client. Pick the one you use.
claude mcp add --transport http bmlt-meetings https://mcp.bmlt.app/mcpcodex mcp add bmlt-meetings --url https://mcp.bmlt.app/mcp{
"mcpServers": {
"bmlt-meetings": {
"url": "https://mcp.bmlt.app/mcp"
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
{
"servers": {
"bmlt-meetings": {
"type": "http",
"url": "https://mcp.bmlt.app/mcp"
}
}
}Add to `.vscode/mcp.json` in your workspace.
{
"mcpServers": {
"bmlt-meetings": {
"url": "https://mcp.bmlt.app/mcp"
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"bmlt-meetings": {
"serverUrl": "https://mcp.bmlt.app/mcp"
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
6 tools
BMLT Meetings exposes 6 tools to a connected agent.
- search_meetings
- Search meetings by address (geocoded server-side) or lat/lng + radius, filtered by weekday, time, format, venue type (in-person / virtual / hybrid), service body, and free text. Returns a curated summary by default (`data_format=full` for the raw BMLT response).
- get_meeting
- Fetch a single meeting by its BMLT `id_bigint`.
- list_formats
- List meeting format codes (Open, Closed, Speaker, Beginners, language tags, ā¦) so callers can map names ā IDs for `search_meetings`.
- list_service_bodies
- List zones / regions / areas / groups so callers can map names ā IDs for `search_meetings`.
- get_server_info
- Capabilities, version, languages, and default coordinates for the configured root server.
- list_root_servers
- Public BMLT root servers known to the aggregator. Useful when the caller wants to switch roots.
Score
62 / 100
Good
- Documentation25/25
- Maintenance13/25
- Trust6/20
- Capability6/15
- Install experience12/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 106 days ago
- Has a release history
- Repository is not archived
- No licence detected
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 6 tool(s) documented
- Provides prompt templates
- Provides resources
- 6 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint ā no local install
Version history
| Versions | Published |
|---|---|
| 0.3.0Latest | May 18, 2026 |
| 0.2.1 | May 17, 2026 |