Skip to content

Add Streamable HTTP transport for Azure Container Apps deployment#2

Open
BenGWeeks wants to merge 6 commits intomainfrom
feature/add-streamable-http-transport
Open

Add Streamable HTTP transport for Azure Container Apps deployment#2
BenGWeeks wants to merge 6 commits intomainfrom
feature/add-streamable-http-transport

Conversation

@BenGWeeks
Copy link
Copy Markdown
Contributor

Overview

This PR implements Streamable HTTP transport to enable Azure Container Apps deployment and Smithery hosting, replacing the deprecated SSE transport approach.

Closes #1

Changes

🆙 Upgraded Dependencies

  • @modelcontextprotocol/sdk: 0.6.01.20.2
    • Access to StreamableHTTPServerTransport
    • Modern Streamable HTTP transport (not deprecated SSE)
  • express: Added for HTTP server
  • @types/express: Added for TypeScript support

✨ New Features

src/http-server.ts - HTTP Server Entry Point

  • Express server with Streamable HTTP transport
  • Single /mcp endpoint supporting:
    • POST - JSON-RPC messages
    • GET - SSE streams for notifications
    • DELETE - Session termination
  • Session management with cryptographically secure UUIDs
  • Graceful shutdown handling
  • Health check endpoint at /health

src/server.ts - Shared Server Logic

  • Extracted common MCP server setup
  • Reusable by both STDIO and HTTP transports
  • All tool registrations in one place

🔄 Updated Components

src/business-central-client.ts

  • Before: AzureCliCredential only
  • After: DefaultAzureCredential
    • Auto-tries: Environment vars → Managed Identity → Azure CLI
    • Works locally (dev) and in cloud (production)

src/index.ts (STDIO Entry Point)

  • Refactored to use shared createMCPServer()
  • Reduced from 300+ lines to 35 lines
  • Maintains exact same functionality

Dockerfile

  • Multi-stage build for optimization
  • Before: STDIO server (won't work in Azure)
  • After: HTTP server on port 3000
  • Proper Node.js Alpine base

smithery.yaml

  • Before: type: stdio (deprecated by Smithery)
  • After: type: http with /mcp endpoint

README.md

  • Added "Azure Container Apps Deployment" section
  • Updated authentication section for DefaultAzureCredential
  • Documented both STDIO and HTTP modes
  • Container deployment examples

Backward Compatibility ✅

No breaking changes - existing users are unaffected:

{
  "mcpServers": {
    "business-central": {
      "type": "stdio",
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@knowall-ai/mcp-business-central"],
      "env": {
        "BC_URL_SERVER": "...",
        "BC_COMPANY": "..."
      }
    }
  }
}

The npx entry point (build/index.js) remains unchanged and uses STDIO transport.

New Capabilities 🚀

1. Azure Container Apps Deployment

# Build and deploy
docker build -t myregistry.azurecr.io/bc-mcp:latest .
docker push myregistry.azurecr.io/bc-mcp:latest

az containerapp create   --name bc-mcp-server   --image myregistry.azurecr.io/bc-mcp:latest   --target-port 3000   --system-assigned

2. Local HTTP Testing

npm run build
npm run start:http
# Server at http://localhost:3000/mcp

3. Smithery Deployment

HTTP transport configuration now allows Smithery to host the server.

Testing

  • TypeScript compilation succeeds
  • STDIO mode builds correctly
  • HTTP mode builds correctly
  • Backward compatibility maintained (npx still works)
  • HTTP server manual testing (needs Azure credentials)
  • Smithery deployment verification
  • Azure Container Apps deployment verification

Technical Details

Transport Architecture

STDIO Transport (src/index.js):

User → npx → StdioServerTransport → createMCPServer() → BusinessCentralClient

HTTP Transport (src/http-server.js):

User → HTTP Request → StreamableHTTPServerTransport → createMCPServer() → BusinessCentralClient

Both use the same server logic, ensuring consistent behavior.

Authentication Flow

Local Development:

  1. Try environment variables (service principal)
  2. Try Managed Identity (fails locally)
  3. Use Azure CLI (succeeds for local dev)

Azure Container Apps:

  1. Try environment variables (none set)
  2. Use Managed Identity (succeeds in Azure)
  3. Azure CLI not available (skipped)

References

Checklist

  • Code compiles without errors
  • Backward compatibility maintained
  • Documentation updated
  • Docker build works
  • Version bumped to 0.2.0
  • Tested with Azure Container Apps
  • Tested with Smithery

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

Implements modern Streamable HTTP transport (MCP v1.20.2) to enable:
- Azure Container Apps deployment
- Azure AI Foundry integration
- Smithery hosted deployment

Key changes:
- Upgrade @modelcontextprotocol/sdk from 0.6.0 to 1.20.2
- Add StreamableHTTPServerTransport in src/http-server.ts
- Extract shared server logic to src/server.ts
- Switch to DefaultAzureCredential for flexible auth (Managed Identity + Azure CLI)
- Update Dockerfile for HTTP server deployment
- Update smithery.yaml to HTTP transport
- Add Azure Container Apps deployment docs to README

Maintains backward compatibility:
- STDIO mode (npx) continues to work as before
- Both transports share the same server implementation

Closes #1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@BenGWeeks BenGWeeks requested a review from Copilot October 25, 2025 16:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements Streamable HTTP transport to enable Azure Container Apps and Smithery deployments, replacing the deprecated SSE approach while maintaining full backward compatibility with existing STDIO mode.

Key Changes:

  • Upgraded @modelcontextprotocol/sdk from 0.6.0 to 1.20.2 for modern HTTP transport support
  • Added Express-based HTTP server with session management alongside existing STDIO mode
  • Switched from AzureCliCredential to DefaultAzureCredential for flexible authentication across local and cloud environments

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/server.ts New shared server factory function containing all MCP tool registrations, used by both STDIO and HTTP transports
src/index.ts Refactored STDIO entry point to use shared server logic, reduced from 300+ to 28 lines
src/http-server.ts New HTTP server with Express, session management, and StreamableHTTPServerTransport
src/business-central-client.ts Changed from AzureCliCredential to DefaultAzureCredential for multi-environment auth support
smithery.yaml Updated from stdio to http transport configuration with /mcp endpoint
package.json Version bump to 0.2.0, SDK upgrade, added express dependencies and start:http script
README.md Added Azure Container Apps deployment section, authentication methods documentation
Dockerfile Multi-stage build optimized for HTTP server deployment instead of STDIO

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@BenGWeeks
Copy link
Copy Markdown
Contributor Author

This Streamable HTTP transport implementation is now being used as a reference for adding the same capability to the mcp-neo4j-agent-memory repository. See knowall-ai/mcp-neo4j-agent-memory#4 for details.

@smithery smithery bot had a problem deploying to production October 25, 2025 16:37 Failure
@smithery smithery bot had a problem deploying to production October 25, 2025 16:44 Failure
…mapping

- Add runtime: container and build configuration
- Add env section to map config values to environment variables
- Add exampleConfig for better user experience
- Fixes deployment error where BC_URL_SERVER and BC_COMPANY were missing
- Remove manual session tracking (transports Map)
- Let StreamableHTTPServerTransport handle sessions via callbacks
- Use single transport instance with sessionIdGenerator
- Wrap initialization in async main() function
- Change to app.all() to handle all HTTP methods (POST, GET, DELETE)

This fixes the 'stream is not readable' error by eliminating double
session management between our code and the SDK.
@smithery smithery bot requested a deployment to production October 25, 2025 16:59 Pending
- Define test config with dummy Business Central credentials
- Add health check endpoint test (GET /health)
- Add MCP initialize test (POST /mcp)
- Verify HTTP 200 responses and session ID generation
- Tests validate the HTTP transport layer works correctly
@smithery smithery bot had a problem deploying to production October 25, 2025 17:13 Failure
- Added test request for MCP tools/list endpoint
- Verifies tool discovery works without authentication
- Helps Smithery scanner properly discover available tools
@smithery smithery bot requested a deployment to production October 25, 2025 17:31 Pending
…ients

- Install cors and @types/cors packages
- Configure CORS middleware with Access-Control-Allow-Origin: *
- Expose mcp-session-id header for browser access
- Allow Content-Type, mcp-session-id, and Accept headers in requests
- Required for Smithery scanner to access session management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@smithery smithery bot had a problem deploying to production October 25, 2025 19:14 Failure
@BenGWeeks BenGWeeks requested a review from Copilot October 25, 2025 19:54
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Create single transport instance with session management
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => {
const sessionId = `bc-${Date.now()}-${Math.random().toString(36).substring(7)}`;
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Session ID generation uses Math.random() which is not cryptographically secure. For session identifiers, use a cryptographically secure random number generator like crypto.randomUUID() or crypto.randomBytes() to prevent session prediction attacks.

Copilot uses AI. Check for mistakes.

// Configure CORS to expose the mcp-session-id header (required for browser-based clients)
app.use(cors({
origin: '*', // Allow all origins (Smithery needs this)
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CORS is configured to allow all origins ('*'). While the comment indicates this is for Smithery, allowing all origins can expose the API to unauthorized cross-origin requests. Consider restricting to specific trusted origins or implementing additional authentication layers.

Copilot uses AI. Check for mistakes.
expectedResponse:
statusCode: 200
headers:
mcp-session-id: ".*" # Should return a session ID
Copy link

Copilot AI Oct 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern .* in the test assertion is too permissive and would match empty strings or malformed session IDs. Consider using a more specific pattern that validates the expected session ID format (e.g., bc-\\d+-[a-z0-9]+ to match the format generated in http-server.ts).

Suggested change
mcp-session-id: ".*" # Should return a session ID
mcp-session-id: "bc-\\d+-[a-z0-9]+" # Should return a session ID in expected format

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Streamable HTTP transport for Azure Container Apps and Smithery deployment

2 participants