Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
eae175e
test: add Express Registration API spec fixture
kshahbw Mar 27, 2026
400d53e
feat: add Express Registration API with no-auth support
kshahbw Mar 27, 2026
506c247
fix: assert Authorization header absent in no-auth Express test
kshahbw Mar 27, 2026
5ddf392
test: add Express tool parameter validation tests
kshahbw Mar 27, 2026
b0f97c6
docs: add Express Registration API to README
kshahbw Mar 27, 2026
fafff28
fix: rename Express verifyCode to verifyRegistrationCode to avoid MFA…
kshahbw Mar 27, 2026
ccae345
feat: zero-to-one auth flow — optional startup credentials + setCrede…
kshahbw Mar 27, 2026
c40263f
fix: address code review — idempotency guard, mutable defaults, test …
kshahbw Mar 27, 2026
d28dab0
feat: serve CLI agent reference as MCP resource
kshahbw Mar 30, 2026
28e03ec
Revert "feat: serve CLI agent reference as MCP resource"
kshahbw Mar 30, 2026
67e276f
docs: add AGENTS.md — structured agent reference for MCP server tools
kshahbw Mar 30, 2026
c3969d3
feat: add dynamic instructions builder for MCP initialization
kshahbw Apr 2, 2026
6a35dfe
feat: add tool profiles for context window management
kshahbw Apr 2, 2026
8cbb1ac
fix: update test_servers resource count assertion (2 → 3)
kshahbw Apr 2, 2026
1c9ed0e
feat: add local spec caching with fallback on network failure
kshahbw Apr 2, 2026
78aef2d
feat: wire dynamic instructions into FastMCP initialization
kshahbw Apr 2, 2026
f8f317e
feat: add in-memory event store with per-session cursors and call state
kshahbw Apr 2, 2026
21d969b
fix: add sleep to event store timestamp test to prevent flakiness
kshahbw Apr 2, 2026
3662386
feat: add Starlette callback routes for messaging and voice webhooks
kshahbw Apr 2, 2026
8922696
feat: add getInboundMessages and getCallbackEvents MCP tools
kshahbw Apr 2, 2026
0d1064c
feat: add generateBXML and respondToCallback voice tools
kshahbw Apr 2, 2026
e43a839
feat: wire transport config, callbacks, voice tools, and instructions…
kshahbw Apr 2, 2026
e4aec88
docs: update AGENTS.md and README with callbacks, voice, hosting, and…
kshahbw Apr 2, 2026
81a9d06
test: add integration tests for full MCP server setup
kshahbw Apr 2, 2026
41c9f39
fix: bundle express spec locally, support local file paths in spec lo…
kshahbw Apr 2, 2026
f6c1197
fix: use FastMCP custom_route for callbacks instead of Starlette mount
kshahbw Apr 2, 2026
70196df
fix: make account_id optional in setCredentials
kshahbw Apr 2, 2026
42f495f
feat: switch to OAuth2 client credentials flow, drop Basic Auth
kshahbw Apr 2, 2026
1e3ed47
feat: add voice, numbers, and toll-free-verification API specs
kshahbw Apr 2, 2026
6d53adb
fix: remove hardcoded tool count from server tests
kshahbw Apr 2, 2026
58ba44d
docs: update all documentation for OAuth2 auth and new capabilities
kshahbw Apr 2, 2026
05ae549
chore: add .DS_Store, .obsidian, .serena, .mcp.json to gitignore
kshahbw Apr 2, 2026
80ee4b8
fix: package bundled specs correctly for uvx installation
kshahbw Apr 2, 2026
a52948c
fix: resolve listCalls tool collision between voice and insights specs
kshahbw Apr 2, 2026
f3291ae
fix: config dict reference bug and test isolation for mid-session auth
kshahbw Apr 2, 2026
15bbff9
fix: bundle AGENTS.md as package data for uvx installs
kshahbw Apr 2, 2026
d82b3a3
feat: add configureCallbacks tool to wire webhook URLs on Bandwidth apps
kshahbw Apr 2, 2026
82364a2
fix: redact secrets from resource://config, remove dead Basic Auth he…
kshahbw Apr 2, 2026
40ef810
fix: rewrite instructions with step-by-step recipes for voice and mes…
kshahbw Apr 2, 2026
ed96862
feat: load all tools at startup regardless of auth state
kshahbw Apr 2, 2026
19cebf3
chore: bump version to 0.2.0 for uvx cache bust
kshahbw Apr 2, 2026
c56a9fa
fix: include all Python modules in wheel build
kshahbw Apr 2, 2026
7553e5c
fix: patch Bandwidth numbers spec for OpenAPI compatibility
kshahbw Apr 2, 2026
a0989df
feat: default to curated 21-tool set, numbers spec opt-in via profile
kshahbw Apr 2, 2026
5e35a45
chore: bump version to 0.3.0
kshahbw Apr 2, 2026
aee6623
fix: use FastMCP lifespan for setup to prevent initialization race
kshahbw Apr 2, 2026
fd7f382
fix: remove broken Numbers API (XML), add follow_redirects, curate pr…
kshahbw Apr 3, 2026
c5029c9
feat: auto-tunnel via cloudflared for dev mode callbacks
kshahbw Apr 3, 2026
104602e
feat: pre-queue BXML before call is answered, fix answer callback flow
kshahbw Apr 3, 2026
d962bc8
fix: register callback routes at module level, not in lifespan
kshahbw Apr 3, 2026
46d02ef
fix: rewrite voice instructions with concrete values from config
kshahbw Apr 3, 2026
1c83dd7
feat: add discovery tools — listApplications, listPhoneNumbers, creat…
kshahbw Apr 3, 2026
77bccac
fix: configureCallbacks uses Dashboard XML API, auto-configure on sta…
kshahbw Apr 3, 2026
e8452b6
chore: remove redundant files and dead imports
kshahbw Apr 8, 2026
c225fdc
refactor: centralize host resolution in urls.py
kshahbw May 27, 2026
648bd56
feat: tighten hosted-mode defaults
kshahbw May 27, 2026
6fb0a97
docs: sync AGENTS.md to CLI post-launch structure
kshahbw May 27, 2026
7c41bff
refactor: BW_ENVIRONMENT support, dashboard via API gateway
kshahbw May 27, 2026
ee4db8a
docs: refresh README tool list, polish AGENTS.md
kshahbw May 27, 2026
0fb6886
feat: rebrand Express → Build, drop in-API verification
kshahbw May 27, 2026
3d65df2
fix: 5 audit findings from end-to-end smoke against stage + prod
kshahbw May 27, 2026
7cac637
docs: document BW_MCP_DEV_TUNNEL local callback tunnel
kshahbw May 28, 2026
eaa2db8
feat: drop MFA tools — API doesn't support OAuth2 yet
kshahbw Jun 10, 2026
1050906
fix: upgrade fastmcp 2.13 → 3.x, patch 3 Snyk vulns (SWI-3723)
kshahbw Jun 10, 2026
5db2c33
fix: configureCallbacks must match the app's ServiceType
kshahbw Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/test-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,5 @@ jobs:

- name: Install Packages and Test
run: |
pip install -r requirements.txt
pip install -r dev-requirements.txt
pip install ".[dev]"
python -m pytest -v
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ venv.bak/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# OS / editor
.DS_Store
.obsidian/
.mcp.json

# Serena
.serena/

# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
Expand Down
214 changes: 131 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ cd mcp-server
### Prerequisites

In order to use the Bandwidth MCP Server, you'll need the following things, set as environment variables.
- Valid Bandwidth API Credentials
- This will be the username and password of your Bandwidth API user
- Valid Bandwidth OAuth2 Client Credentials
- You will need a client ID and client secret for your Bandwidth API application
- For more info on creating API credentials, see our [Credentials](https://dev.bandwidth.com/docs/credentials) page
- Bandwidth Account ID
- The ID of the account you'd like to make API calls on behalf of
- Your account ID is auto-discovered from JWT claims after authentication — you do not need to provide it

### Configuration

Expand All @@ -33,19 +32,24 @@ The server will respect both system environment variables and those configured v
The following variables will be required to use the server:

```sh
BW_USERNAME # Your Bandwidth API User Username
BW_PASSWORD # Your Bandwidth API User Password
BW_CLIENT_ID # Your Bandwidth OAuth2 client ID
BW_CLIENT_SECRET # Your Bandwidth OAuth2 client secret
```

The following variables are optional or conditionally required:

```sh
BW_ACCOUNT_ID # Your Bandwidth Account ID. Required for most API operations.
BW_NUMBER # A valid phone number on your Bandwidth account. Used with our Messaging and MFA APIs. Must be in E164 format.
BW_MESSAGING_APPLICATION_ID # A Bandwidth Messaging Application ID. Used with our Messaging and MFA APIs.
BW_VOICE_APPLICATION_ID # A Bandwidth Voice Application ID. Used with our MFA API.
BW_MCP_TOOLS # The list of MCP tools you'd like to enable. If not set, all tools are enabled.
BW_MCP_EXCLUDE_TOOLS # The list of MCP tools you'd like to exclude. Takes priority over BW_MCP_TOOLS.
BW_ACCOUNT_ID # Your Bandwidth Account ID. Optional — auto-discovered from JWT claims after authentication.
BW_NUMBER # A valid phone number on your Bandwidth account. Used with our Messaging API. Must be in E164 format.
BW_MESSAGING_APPLICATION_ID # A Bandwidth Messaging Application ID. Used with our Messaging API.
BW_VOICE_APPLICATION_ID # A Bandwidth Voice Application ID. Used to auto-configure voice callbacks in hosted mode.
BW_MCP_PROFILE # Named tool preset (voice, messaging, lookup, onboarding, recordings, full). Comma-separated to combine.
BW_MCP_TOOLS # Explicit tool allowlist (comma-separated operationIds). Overrides BW_MCP_PROFILE.
BW_MCP_EXCLUDE_TOOLS # Explicit tool denylist (comma-separated). Takes priority over BW_MCP_TOOLS and profiles.
BW_ENVIRONMENT # `test` or `uat` to target Bandwidth's test environment. Defaults to prod.
BW_API_URL # API gateway override. Also serves the Dashboard XML API under /api/v2.
BW_VOICE_URL # Voice API base override.
BW_MESSAGING_URL # Messaging API base override.
```

#### Including or Excluding Tools
Expand Down Expand Up @@ -85,6 +89,14 @@ BW_MCP_EXCLUDE_TOOLS=createLookup,getLookupStatus
--exclude-tools createLookup,getLookupStatus
```

**Account Creation Flow (Build Registration)**

Bandwidth Build is the free voice-first trial. Use this profile when the user has no credentials yet. The MCP only exposes `createRegistration` to kick things off — SMS verification, password set, and API credential generation all happen in pages Bandwidth links the user to. The agent shouldn't try to consume the SMS code over the API; it belongs to the user's browser flow.

```sh
BW_MCP_TOOLS=createRegistration
```

## Using the Server

Below you'll find instructions for using our MCP server with different common AI agents, as well as instructions for running the server locally. For usage with AI agents, it is recommended to use a combination of [uv](https://github.com/astral-sh/uv?tab=readme-ov-file#uv) and environment variables to start and configure the server respectively.
Expand Down Expand Up @@ -131,8 +143,8 @@ Then follow the prompts like the example below.
"command":"uvx",
"args": ["--from", "/path/to/mcp-server", "start"],
"env": {
"BW_USERNAME": "<insert-bw-username>",
"BW_PASSWORD": "<insert-bw-password>",
"BW_CLIENT_ID": "<insert-bw-client-id>",
"BW_CLIENT_SECRET": "<insert-bw-client-secret>",
"BW_MCP_TOOLS": "tools,to,enable",
"BW_MCP_EXCLUDE_TOOLS": "tools,to,exclude",
}
Expand Down Expand Up @@ -161,8 +173,8 @@ uvx --from /path/to/mcp-server start
"command": "uvx",
"args": ["--from", "/path/to/mcp-server", "start"],
"env": {
"BW_USERNAME": "<insert-bw-username>",
"BW_PASSWORD": "<insert-bw-password>",
"BW_CLIENT_ID": "<insert-bw-client-id>",
"BW_CLIENT_SECRET": "<insert-bw-client-secret>",
"BW_MCP_TOOLS": "tools,to,enable",
"BW_MCP_EXCLUDE_TOOLS": "tools,to,exclude",
}
Expand All @@ -186,8 +198,8 @@ uvx --from /path/to/mcp-server start
"command": "uvx",
"args": ["--from", "/path/to/mcp-server", "start"],
"env": {
"BW_USERNAME": "<insert-bw-username>",
"BW_PASSWORD": "<insert-bw-password>",
"BW_CLIENT_ID": "<insert-bw-client-id>",
"BW_CLIENT_SECRET": "<insert-bw-client-secret>",
"BW_MCP_TOOLS": "tools,to,enable",
"BW_MCP_EXCLUDE_TOOLS": "tools,to,exclude",
}
Expand All @@ -212,11 +224,11 @@ Once these are installed, create a virtual environment using:
python -m venv .venv
```

Then activate and install the required packaged from the `requirements.txt` file.
Then activate and install the project with its dependencies.

```sh
source .venv/bin/activate
pip install -r requirements.txt
pip install .
```

After all packages are installed in the virtual environment, you can run the server locally using:
Expand All @@ -234,68 +246,104 @@ then you can start the server by running the following command from the root dir
uvx --from ./ start
```

## Hosted Mode

Run the server over HTTP to enable remote access and webhook callbacks:

```bash
BW_MCP_TRANSPORT=streamable-http \
BW_MCP_PORT=8080 \
BW_MCP_BASE_URL=https://your-server.example.com \
BW_CLIENT_ID=your_client_id \
BW_CLIENT_SECRET=your_client_secret \
python src/app.py
```

### Local Callback Tunnel (dev only)

Voice and messaging callbacks need Bandwidth to reach your server over a public
URL. In production you set `BW_MCP_BASE_URL`. For local development, set
`BW_MCP_DEV_TUNNEL=1` and the server will open an ephemeral
[`cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/)
tunnel, use the resulting `*.trycloudflare.com` URL as `BW_MCP_BASE_URL`, and
auto-wire your voice app's callbacks to it — no ngrok needed.

```bash
BW_MCP_TRANSPORT=streamable-http \
BW_MCP_DEV_TUNNEL=1 \
BW_CLIENT_ID=your_client_id \
BW_CLIENT_SECRET=your_client_secret \
python src/app.py
```

Requires `cloudflared` on your PATH (`brew install cloudflared` on macOS). If
it's missing, the server logs a warning and starts without a tunnel. This is
for **development and testing only** — production deployments should use a real
host via `BW_MCP_BASE_URL`. The tunnel never starts unless `BW_MCP_DEV_TUNNEL`
is set.

### Tool Profiles

Reduce context window pressure with named presets:

```bash
BW_MCP_PROFILE=messaging # SMS/MMS tools only
BW_MCP_PROFILE=voice # Voice + BXML tools
BW_MCP_PROFILE=onboarding # Account creation
BW_MCP_PROFILE=lookup # Number intelligence
BW_MCP_PROFILE=messaging,voice # Combine profiles
```

Profiles set via `BW_MCP_PROFILE` env var or `--profile` CLI flag. Use `BW_MCP_TOOLS` to override with specific tool names.

## Tools List

## **Multi-Factor Authentication (MFA)**
- `generateMessagingCode` - Send MFA code via SMS
- `generateVoiceCode` - Send MFA code via voice call
- `verifyCode` - Verify a previously sent MFA code

## **Phone Number Lookup**
- `createLookup` - Create a phone number lookup request
- `getLookupStatus` - Get status of an existing lookup request

## **Voice & Call Management**
- `listCalls` - Returns a list of call events with filtering options
- `listCall` - Returns details for a single call event

## **Reporting & Analytics**
- `getReports` - Get history of created reports
- `createReport` - Create a new report instance
- `getReportStatus` - Get status of a report
- `getReportFile` - Download report file
- `getReportDefinitions` - Get available report definitions

## **Media Management**
- `listMedia` - List your media files
- `getMedia` - Download a specific media file
- `uploadMedia` - Upload a media file
- `deleteMedia` - Delete a media file

## **Messaging**
- `listMessages` - List messages with filtering options
- `createMessage` - Send SMS/MMS messages
- `createMultiChannelMessage` - Send multi-channel messages (RBM, SMS, MMS)

## **Address Management**
- `getAddressFields` - Get supported address fields by country
- `validateAddress` - Validate an address and get excluded features
- `listAddresses` - List all addresses
- `createAddress` - Create an address
- `getAddress` - Get an address by ID
- `updateAddress` - Update an address
- `listCityInfo` - List city info search results

## **Compliance**
- `listDocumentTypes` - List all accepted document types and metadata requirements
- `listEndUserTypes` - List all End user types and accepted metadata
- `listEndUserActivationRequirements` - List requirements for End user activation
- `getComplianceDocumentMetadata` - Get metadata of uploaded documents
- `updateComplianceDocument` - Modify document data and file
- `downloadComplianceDocuments` - Download document using document ID
- `createComplianceDocument` - Upload a document with metadata
- `listComplianceEndUsers` - List all End users of an account
- `createComplianceEndUser` - Create an End user
- `getComplianceEndUser` - Retrieve an End User by ID
- `updateComplianceEndUser` - Update End user details

## **Requirements Packages**
- `listRequirementsPackages` - List all requirements packages
- `createRequirementsPackage` - Create a requirements package
- `getRequirementsPackage` - Retrieve a requirements package
- `patchRequirementsPackage` - Update Requirements package
- `getRequirementsPackageAssets` - Get assets attached to requirements package
- `attachRequirementsPackageAsset` - Attach an asset to requirements package
- `detachRequirementsPackageAsset` - Detach an asset from requirements package
- `validateNumberActivation` - Validate number activation requirements
- `getRequirementsPackageHistory` - Get history of a requirements package
Tools are grouped into profiles that mirror the workflows you'd use the server for.
Loading a single profile keeps your agent's context small. The full agent reference
— including auth model, error codes, and the "trust nothing" guidance for async
calls — lives in [`src/specs/AGENTS.md`](src/specs/AGENTS.md).

The default tool set is `voice` + `messaging` + `lookup` (plus
`setCredentials` / `clearCredentials`, always loaded). Override with
`BW_MCP_PROFILE`, `BW_MCP_TOOLS`, or `BW_MCP_EXCLUDE_TOOLS`.

### Session management (always loaded)
- `setCredentials` — authenticate the session (stdio transport only)
- `clearCredentials` — log out of the session

### Profile: `onboarding` (no auth required)
Kicks off a new Bandwidth Build account. Only one tool is exposed — SMS verification, password set, and API credential generation all happen in the user's browser. The agent hands off after the kickoff.
- `createRegistration` — submit contact details; Bandwidth then SMSes an OTP and emails a password-set link

### Profile: `voice`
- `listApplications` / `createApplication` — find or create a voice app
- `listPhoneNumbers` — find numbers on the account
- `createCall` — place an outbound call
- `getCallState` — read current call state (always poll after `createCall`)
- `listCalls` — list call events with filtering
- `updateCall` / `updateCallBxml` — redirect, hang up, or replace BXML
- `generateBXML` — build valid BXML from a verb list
- `respondToCallback` — queue a BXML response for an active callback (first-write-wins)
- `getCallbackEvents` — read recent voice / messaging callback events
- `configureCallbacks` — point an application's webhook URLs at this server

### Profile: `messaging`
- `createMessage` — send SMS / MMS
- `listMessages` — query message history
- `getInboundMessages` — read inbound messages captured by this server
- `listMedia` / `getMedia` / `uploadMedia` / `deleteMedia` — manage MMS media
- `configureCallbacks` — point an application's callbacks at this server

### Profile: `lookup`
- `createSyncLookup` — one-shot lookup for a small input set
- `createAsyncBulkLookup` — kick off a bulk lookup
- `getAsyncBulkLookup` — poll a bulk lookup

### Profile: `recordings`
- `listCallRecordings` / `getCallRecording` — list / inspect recordings
- `downloadCallRecording` — download the media
- `deleteRecording` — remove a recording
- `transcribeCallRecording` / `getRecordingTranscription` — request and read transcription

See [`src/specs/AGENTS.md`](src/specs/AGENTS.md) for argument-level guidance, polling
patterns, and the structured error shape the server returns on failure.
19 changes: 0 additions & 19 deletions common_use_cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,6 @@ BW_MCP_TOOLS=createLookup,getLookupStatus
--tools createLookup,getLookupStatus
```

## Utilizing our MFA Service

To create and verify multi-factor authentication codes, you'll need our three MFA tools.
- `generateMessagingCode` - Used to generate and send an MFA code via SMS
- `generateVoiceCode` - Use to generate and send an MFA code via a phone call
- `verifyCode` - Verify an MFA code sent with one of the previous tools

Generating Messaging and Voice codes requires the `BW_MESSAGING_APPLICATION_ID` and `BW_VOICE_APPLICATION_ID` environment variables respectively.
Both of these tools also require `BW_ACCOUNT_ID` and `BW_NUMBER`.

**Enabling these tools**
```sh
# Environment Variable
BW_MCP_TOOLS=generateMessagingCode,generateVoiceCode,verifyCode

# CLI Flag
--tools generateMessagingCode,generateVoiceCode,verifyCode
```

## Adding a Business End User

To add an end user, you'll need three specific Compliance endpoints.
Expand Down
4 changes: 0 additions & 4 deletions dev-requirements.txt

This file was deleted.

24 changes: 20 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "bw-mcp-server"
version = "0.1.0"
version = "0.3.0"
description = "Bandwidth MCP Server"
authors = [
{name = "Bandwidth", email = "dx@bandwidth.com"}
Expand All @@ -9,16 +9,32 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"httpx~=0.28.0",
"fastmcp~=2.13.0",
"mcp~=1.23.0",
"fastmcp~=3.2",
"mcp~=1.24",
"pyyaml~=6.0.0",
"werkzeug>=3.1.4"
]

[project.scripts]
start = "app:main"

[dependency-groups]
[tool.setuptools]
py-modules = [
"app", "config", "servers", "server_utils", "resources",
"instructions", "profiles", "oauth", "event_store", "callbacks", "tunnel",
]
packages = ["specs", "tools"]

[tool.setuptools.package-dir]
"" = "src"

[tool.setuptools.package-data]
specs = ["*.yml", "*.md"]

[tool.pytest.ini_options]
pythonpath = ["."]

[project.optional-dependencies]
dev = [
"black>=25.1.0",
"pytest>=8.4.1",
Expand Down
5 changes: 0 additions & 5 deletions requirements.txt

This file was deleted.

Loading
Loading