SWI-10226 feat: add Express Registration API + zero-to-one auth flow#6
Open
SWI-10226 feat: add Express Registration API + zero-to-one auth flow#6
Conversation
Adds requires_auth parameter to _create_server, registers Express Registration API (no-auth) in api_server_info, and adds Express-specific tests. Total tool count updated to 49 (Express adds 2 net new tools; verifyCode deduplicates with MFA). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… collision The Express and MFA APIs both had operationId: verifyCode, causing FastMCP to silently drop one during import. Renamed to verifyRegistrationCode so all 50 tools are accessible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ntials tool Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fidelity
- Add idempotency guard to _reload_authenticated_servers (prevents tool
duplication on repeated setCredentials calls)
- Fix mutable default config={} in _create_server and create_bandwidth_mcp
- Fix Express tests to use requires_auth=False (matches production config)
- Move warnings import to top of config.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Adds AGENTS.md (structured reference for AI agents using the bw CLI) and exposes it as resource://cli_agent_reference. Covers command semantics, prerequisite graph, workflows, error patterns, and limitations so agents can plan multi-step operations without trial and error. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This reverts commit 7664a2f.
Adds AGENTS.md as a comprehensive reference for AI agents using the MCP server: tool groups, required env vars, dependency order, common workflows, error patterns, and limitations. Also registers it as a FunctionResource at resource://mcp_agent_reference so agents can read it programmatically mid-session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s, voice Covers instructions-based agent routing (FastMCP instructions field), HTTP transport for hosted deployment, callback server for inbound events, programmable voice via BXML with barge-in and redirect-chain patterns, tool profiles, and local spec caching. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop conversation summarization (LLM manages its own context), single-tenant only, callback server over relay, in-memory event store, light SSML by default. Two open questions remain. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per-session read cursors on the event store so multiple MCP sessions (two IDE windows, etc.) don't race on callback events. First-write-wins on voice responses prevents two agents from responding to the same call. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers: dynamic instructions, tool profiles, spec caching, event store, callback routes, callback tools, BXML generation, voice tools, transport config, docs updates, and integration tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds src/instructions.py with build_instructions() that generates context-aware MCP instructions based on loaded tools and config presence. Also adds pythonpath config to pyproject.toml so src imports resolve consistently across all test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces resolve_profile() with named presets (messaging, voice, onboarding, lookup, full) and wires --profile CLI flag + BW_MCP_PROFILE env var into get_enabled_tools() as a fallback when no explicit tool list is provided. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Config resource was added after the test was written. Fixes 5 pre-existing failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Writes cleaned specs to ~/.bw-mcp/spec-cache after a successful fetch and falls back to the cached version when the network is unavailable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Calls build_instructions after setup() and _reload_authenticated_servers() so mcp.instructions reflects the actual loaded tool set at runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ring-buffer backed EventStore with TTL, per-session read cursors for multi-session safety, and first-write-wins BXML locking on CallState objects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements BXML generation from verb descriptors and a callback-response flow for active voice calls, with full test coverage across all supported verb types and edge cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… into app Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ader Express Registration spec isn't published to dev.bandwidth.com yet. Bundle it in src/specs/ and load directly from disk. fetch_openapi_spec now handles local paths before attempting HTTP fetch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FastMCP.mount() expects another FastMCP server, not a raw Starlette app. Refactored to use @mcp.custom_route() decorator which registers routes directly on the MCP server's HTTP transport. Callback routes are now registered during setup() alongside tools, always available in HTTP mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent can authenticate with just username/password and discover the account ID from the API afterward. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
setCredentials now takes client_id and client_secret, exchanges them for a Bearer token via POST /api/v1/oauth2/token, and extracts account IDs from JWT claims — same flow as the Bandwidth CLI. Account ID is discovered automatically, not required from the user. Startup OAuth: if BW_CLIENT_ID and BW_CLIENT_SECRET env vars are set, token exchange happens during setup(). All API requests use Bearer auth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds createCall and 27 other voice tools, numbers management, and toll-free verification to the server. All specs fetched from dev.bandwidth.com at startup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test now asserts tools > 0 and validates filtering behavior directly instead of predicting a total count that breaks when specs change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Basic Auth (BW_USERNAME/BW_PASSWORD) with OAuth2 client credentials (BW_CLIENT_ID/BW_CLIENT_SECRET) across AGENTS.md, README.md, and src/instructions.py. Account ID is now auto-discovered from JWT claims. Also updates AGENTS.md to reflect new Voice API (28 tools), Numbers API, Toll-Free Verification, and removes stale limitations (no voice tools, no webhook registration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
specs/ dir wasn't included when installed via uvx. Added specs as a proper Python package with __init__.py so setuptools includes the YAML files. Resolves spec path via module import instead of __file__. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Voice and insights both define listCalls/listCall. Exclude the insights duplicates via per-spec exclude_tools so voice's versions win. Also reorder specs so voice loads before insights. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
setup() now mutates _config via update() instead of reassigning, so setCredentials and _reload_authenticated_servers share the same dict. Fixed integration test isolation — clear module state between tests. Production mid-session flow verified: 8 → 96 tools after setCredentials. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AGENTS.md was referenced relative to repo root which doesn't exist in uvx cache. Moved into specs/ package alongside express.yml. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PATCHes the Bandwidth Applications API to point callback URLs at this server's public URL. Agent calls configureCallbacks(application_id, base_url) and webhooks are wired — no manual dashboard config needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lper Config resource was exposing BW_CLIENT_SECRET and BW_ACCESS_TOKEN to any MCP client reading resource://config. Now redacts sensitive values. Also removed unused create_auth_header (Basic Auth is gone). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…saging Agent was burning 170k tokens exploring source code to figure out how to make a call. Instructions now give explicit step-by-step sequences: find your number → configure callbacks → generate BXML → createCall. Header explicitly tells agent to use tools, not read source code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tools are now always registered from OpenAPI specs, even without credentials. Auth is checked at API call time (401), not at startup. This eliminates the mid-session reload problem — clients always see the full tool list. Credentials come from env vars in the MCP config. setCredentials remains for express registration flows but is no longer the primary auth path. Removed _reload_authenticated_servers and requires_auth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pyproject.toml only listed packages (specs/, tools/) but not the loose .py modules (app, servers, config, etc.). Added py-modules list so uvx installs the complete server, not just the subpackages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Numbers spec has malformed $refs (strings instead of dicts), missing response descriptions, and allOf misplaced inside properties. Added three spec patching functions to _clean_openapi_spec that fix these patterns. Numbers API now loads 343 tools successfully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
430 tools was unusable. Default now loads a task-oriented set covering messaging, voice, lookup, MFA, number discovery, and callbacks (21 tools). Numbers API (343 tools) is opt-in via BW_MCP_PROFILE=numbers. Profiles are task-oriented, not API-oriented. dict.fromkeys() for dedup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
setup() was running in asyncio.run() before mcp.run(), causing "request before initialization was complete" errors and -32602 on every tool call. Moved setup into a lifespan context manager so it runs inside FastMCP's event loop during proper initialization. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ofiles Numbers API is XML-based — from_openapi sends JSON which the API rejects. Disabled until we build a proper XML adapter. Added follow_redirects=True to httpx clients for APIs that use 303 redirects. Profiles now cherrypick operationIds matching the CLI command surface. Created voice app d27b5ce6-167f-4664-9c03-c60b472f6fae on account 9901287. Verified createCall works with +19192964738 as from number. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When no BW_MCP_BASE_URL is set and transport is HTTP, the server automatically starts a Cloudflare quick tunnel. Zero user config — callbacks just work. Production deploys set BW_MCP_BASE_URL instead. Verified end-to-end: tunnel URL → configureCallbacks → createCall. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
respondToCallback now auto-creates call state if it doesn't exist, allowing agents to queue BXML before the callee answers. The answer callback checks for pre-queued BXML and serves it immediately instead of redirecting. Instructions updated with correct sequence: generate BXML → createCall → respondToCallback (before answer). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
custom_route must be called before mcp.run() so Starlette includes the routes in its app. Moving from lifespan to module level fixes 404 on callback URLs. Verified: Bandwidth callback received and returned pre-queued BXML — voice call works end to end. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent needs to know exactly where to find accountId, applicationId, from number, and answerUrl. Instructions now point to specific config keys and give the exact answerUrl pattern. Added auto_gather guidance for one-shot vs conversational calls. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eApplication Agent can now discover phone numbers and voice applications from the account instead of requiring pre-configured env vars. createApplication handles the edge case where no Voice-V2 app exists — it creates one with callback URLs auto-pointed at the server. Instructions updated: agent reads config, discovers resources, creates app if needed, then makes the call. Zero pre-configuration beyond credentials. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rtup configureCallbacks was hitting the Voice v2 JSON API which doesn't actually update app callback URLs. Switched to Dashboard XML API (GET current app, PUT with updated URLs). Server auto-configures voice app callbacks on startup when BW_VOICE_APPLICATION_ID and BW_MCP_BASE_URL are set. Added gatherUrl to auto-generated Gather BXML so Bandwidth knows where to POST speech results. Conversation flow verified: greeting → speech capture → response. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop requirements.txt and dev-requirements.txt in favor of pyproject.toml with [project.optional-dependencies]. Update CI to pip install ".[dev]". Remove root AGENTS.md (stale duplicate of src/specs/AGENTS.md). Clean up 5 unused imports across source files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
requires_authflag on_create_server— Express requires no authenticationBW_USERNAME/BW_PASSWORD, Express tools are available immediatelysetCredentialstool so agents can inject credentials mid-session after Express registration, triggering loading of authenticated API serversverifyCode→verifyRegistrationCodeto avoid operationId collision with MFA'sverifyCodeChanges
src/servers.py: Addedrequires_authparam, Express inapi_server_info, explicit credential checksrc/config.py: Credentials now optional (warns instead of raising)src/app.py: Reload callback for post-registration credential injectionsrc/tools/credentials.py:setCredentialstool with idempotency guardtest/fixtures/express.yml: Express OpenAPI spec fixture (3 endpoints)test/test_express.py: 5 Express-specific teststest/test_config.py: Optional credentials teststest/test_credentials.py: setCredentials tool teststest/test_servers.py: Updated tool count (50), added express mockREADME.md: Express Registration docs + tool filtering exampleTest plan
🤖 Generated with Claude Code