Host an HTTP MCP server
App Service Builder can host an HTTP application that exposes a read-only Model Context Protocol (MCP) tool to a remote client. This is an application you deploy, not a Builder-managed MCP service or management endpoint.
At a glance
- Use the
mcp-serversample with the official MCP TypeScript SDK v2 and Node 24. - Serve stateless Streamable HTTP JSON responses at
/mcpand liveness at/health. - Authenticate tool requests independently of health checks and management access.
- Use this guide for Private Preview development/test only after your contact confirms the environment and sample are ready for you.
Prerequisites
Use your Private Preview subscription and an available region.
Open the Portal or install Builder CLI.
The source belongs to the HTTP MCP server repository.
See the sample catalog for choices and target-verification prerequisites.
Keep the sample's source, lockfile, client, tests, README, and root builder.yaml together.
The sample is an HTTP MCP endpoint, not a hosted stdio process. It demonstrates bounded POST request/response operation, not long-lived streams, persistent sessions, resumability, or production workloads. Local client success is not live App Service Builder compatibility.
Quick start locally
Fork the sample repository to your account and clone your fork.
Run from its repository root, keeping the root builder.yaml with the application.
With Node 24:
npm ci --ignore-scripts
npm test
export APP_API_TOKEN="$(node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))")"
npm start
In a second terminal with the same app token, run:
curl http://127.0.0.1:8080/health
node client.mjs
The included client initializes the MCP connection, lists text_length, and calls it with Hello.
The returned length is 5.
Check compatibility when choosing another MCP client.
Stop the server with Ctrl+C.
Endpoint and configuration
| Item | Sample contract |
|---|---|
POST /mcp | Stateless Streamable HTTP JSON-RPC requests with JSON responses |
text_length | Read-only count of Unicode code points; at most 1000 UTF-16 code units of input; no file, network, or state operations |
GET /health | Public process liveness: status 200 and OK body |
GET / | Public sample identifier |
GET or DELETE /mcp | Status 405; no session/event-stream lifecycle |
APP_API_TOKEN | Required app-side bearer credential, at least 32 characters |
PUBLIC_ORIGIN | Optional exact HTTPS origin without a trailing slash, allowed when a client supplies Origin |
MCP_URL | Client-side endpoint; defaults to http://127.0.0.1:8080/mcp |
PLATFORM_ACCESS_TOKEN | Optional client-side platform credential sent separately in X-Embr-Authorization |
The server limits JSON request bodies to 8192 bytes, including chunked requests.
Supplied invalid, empty, or null Origin values return 403 on every route, following the MCP transport security requirements.
The public /health endpoint returns only process liveness, without invoking a tool or returning configuration.
The process binds 0.0.0.0:8080 for the web component.
App-side bearer-token checks are independent of Origin validation.
The sample uses ordinary HTTP and the official SDK without interpreting Host or forwarding headers.
Keep local use on an isolated network.
The sample does not grant browser CORS access.
Deploy the web component
Commit and push your changes to your fork; deployment uses committed GitHub source.
Authorize your fork using source control and keep the app source root at the repository root (".").
The included manifest uses:
components:
- name: web
role: web
platform: nodejs
platformVersion: "24"
path: /
run:
port: 8080
start: node server.mjs
healthCheckPath: /health
healthCheckStatusCodes: [200]
Keep the fork, branch, and root builder.yaml you prepared above.
Builder deploys committed GitHub source, not uncommitted local files.
- Portal
- Builder CLI
Open the App Service Builder Portal.
Your Azure account needs permission to create and deploy Microsoft.Web/builderApps resources.
Creating a resource group also requires permission to create groups in the selected subscription.
- Sign in to the Portal with the Microsoft account authorized for your subscription.
- Select the subscription and choose Create.
- In Connect GitHub, sign in to GitHub and authorize the OAuth App. Keep Use my repository selected to deploy the fork you prepared above. Choose the account or organization that owns your fork, then select that repository and branch. Use Change account if you need a different GitHub account, or Refresh access after changing repository permissions. Select Next.
- In Choose approach, confirm the selected repository and branch. Keep Let Builder handle it selected. The App Service Builder GitHub App needs access to this repository to read your code, build it, and deploy your app. Signing in to GitHub alone does not grant this access. If prompted, select Install GitHub App and grant access to your selected repository, or add it to an existing installation. Close the GitHub window to refresh access, or select Refresh access after approval. Select Next when Builder is connected and GitHub App access confirmed are shown.
- In App details, enter an app name and select the subscription, resource group, and offered location. To create a resource group, select Create new, enter its name, and choose Resource group region. Select Add. The group appears as (new) and is created when you submit. Its region stores the resource group's metadata and does not change the app's region. Select Next.
- On Review and create, confirm the app, repository, branch, subscription, resource group, and app region. Select Create and deploy.
- Open the app's Deployments page and wait for the deployment to succeed. In Versions, inspect the source revision, component results, and build logs. Select Deploying to open an in-progress deployment's steps and logs. On Overview, use Browse to open the app URL.
Already prepared a fork? Keep Use my repository selected so you deploy that source, not a second copy. To start with a new sample instead, follow Create from a Portal template. Use GitHub Actions is marked Coming soon; use the Builder-managed approach for this preview.
If group creation fails, app creation does not start. If the group was created before a later failure, it remains in Azure; cancelling does not delete it. Check the existing group and app before retrying, and inspect a group's contents before deleting it separately.
See GitHub authorization requirements for organization approvals and SSO. If app creation succeeds but deployment cannot start, read the error and retry deployment on the existing app rather than creating a duplicate. For “Source revision does not contain builder.yaml”, push the missing manifest before retrying.
Choose a browser or terminal download.
Download in your browser: download Builder CLI 0.0.2 (azure-builder-cli-0.0.2.tgz) from the Private Preview release. Choose the CLI archive, not GitHub's automatic Source code ZIP or tarball.
Download from your terminal: with GitHub CLI installed, run this command in the directory where you want to save the archive:
gh release download builder-cli-v0.0.2-preview.1 --repo microsoft/azure-app-service-builder --pattern "azure-builder-cli-0.0.2.tgz"
The command downloads the CLI archive into the current directory.
It refuses to overwrite an existing file; choose another directory if the archive is already present.
Release archives use the name azure-builder-cli-<version>.tgz.
Use the same portable npm archive on Windows, Linux, and macOS; there is no separate OS/CPU-specific CLI download.
Install Node.js 22 or later, including npm.
Install or upgrade using your downloaded archive directly:
npm install --global "<path-to-downloaded-file.tgz>"
builder --version
builder --version should report 0.0.2.
Replace <path-to-downloaded-file.tgz> with the local path to your downloaded azure-builder-cli-0.0.2.tgz.
For the terminal download above, use ./azure-builder-cli-0.0.2.tgz while in the same directory.
Obtain preview access and confirm your approved subscription and location with your preview contact.
Do not extract the archive.
npm registry access is still required for JavaScript dependencies; access to Builder's private source repository is not required.
Installing the CLI does not grant preview access or install the separately distributed agent skills plugin.
The .NET runtime is needed only for local builder manifest commands.
You can install Builder and run builder login, builder init, and
builder app deploy without .NET.
The archive includes the local manifest analyzer and its dependencies, but not the .NET runtime.
For optional local manifest validation, install a compatible .NET 10 runtime and keep dotnet on PATH.
The analyzer requires Microsoft.NETCore.App 10.0.0 or a compatible runtime.
A .NET SDK is not required to use Builder; an existing compatible SDK's included runtime is sufficient.
Check installed runtimes with:
dotnet --list-runtimes
Use your installed Builder CLI and GitHub CLI (gh) to connect your source.
The App Service Builder GitHub App must have access to your selected repository so Builder can read, build, and deploy its code.
See GitHub authorization requirements if access has not been granted.
Reuse a resource group, or create one first with Azure CLI:
az login
az group create --subscription "<subscription-id>" \
--name "<resource-group>" --location "eastus2"
Replace the subscription and resource-group placeholders with your values.
You can use westus2 instead of eastus2.
Only this optional resource-group creation step requires Azure CLI and permission to create resource groups.
Builder CLI uses the group you select; it does not create it.
If you have not already cloned your prepared fork, copy its HTTPS clone URL from GitHub and run:
git clone "<your-fork-https-url>"
cd "<local-repository-directory>"
Run builder init from inside an initialized Git worktree.
GitHub sign-in does not select a local repository.
If your terminal is in your home directory, open your cloned app folder or change to it first.
Do not initialize Git in your home directory to get past the worktree error.
Run these commands from your app's checkout:
builder --help
builder login
builder account show
builder source login github
builder init
Follow the prompts and confirm the app name, Azure subscription, resource group, approved location, and GitHub source.
Select your prepared fork and its pushed branch.
Keep the app name, subscription ID, and resource group for the deployment commands below.
Initialization creates or reuses the app and connects source; it does not deploy or upload local changes.
For explicit automation, see non-interactive setup.
builder init does not automatically analyze or modify builder.yaml.
Deployment requires builder.yaml at the selected source root.
Review your manifest, then commit and push any changes you want to deploy.
Optionally validate the prepared manifest locally without changing files or running repository code. This step requires the separate .NET runtime described in the installation instructions; login, setup, and deployment do not:
builder manifest validate
Request deployment and read the app:
builder app deploy --name "<app-name>" \
--subscription "<subscription-id>" --resource-group "<resource-group>"
builder app show --name "<app-name>" \
--subscription "<subscription-id>" --resource-group "<resource-group>"
Deployment waits by default.
After success, use the runtime URL returned by app show for the checks below.
For a failure, inspect build and deployment diagnostics.
The CLI reference lists selectors, diagnostics, and flags.
For API automation, use the ARM API deployment guide with this same fork, branch, and source root ".".
ARM and Bicep infrastructure templates provision the resource; separately authorize and connect this source, then deploy it through CLI or ARM.
Set APP_API_TOKEN using application variables. PUBLIC_ORIGIN is optional for clients that supply Origin; the included server-side client does not require it.
Choose Save and deploy to capture the desired values in a new version.
Variables are shared by build and runtime; there is no runtime-only secret switch.
Keep credentials out of source, build logs, browser assets, URLs, and tool results.
Wait for deployment success and verify /health through the actual target before testing MCP.
Health reports process liveness; also test an authenticated tool call.
Connect and authorize a client
Connect a client such as VS Code to the sample app you deployed, then discover and call its tool.
The name sample-mcp below identifies your app in the client; it is not a Builder-provided MCP service.
The App Service MCP tutorial uses the same pattern: connect a client, select the tool, and inspect its result.
Connect from VS Code
-
Open a project in VS Code with Copilot enabled. The remote server does not need to be cloned into this project.
-
Provide
APP_API_TOKENsecurely in the environment of the VS Code process. Use the same token configured in your deployed app; do not paste it into chat or a checked-in file. -
Configure an HTTP server in your personal MCP configuration, using the VS Code configuration instructions. For a VS Code-format
mcp.json, the server entry is:{
"servers": {
"sample-mcp": {
"type": "http",
"url": "https://<your-app-host>/mcp",
"headers": {
"Authorization": "Bearer ${env:APP_API_TOKEN}"
}
}
}
}Replace
<your-app-host>with the host returned for your deployment. For a Copilot portable configuration instead, usemcpServersand${APP_API_TOKEN}as described in the linked reference; do not mix the formats. -
Start the server connection using your client's MCP controls and review the trust prompt. Confirm that
text_lengthappears in the available tools. -
In Copilot chat, ask:
Use the sample-mcp server's text_length tool to count the characters in Hello.Approve the tool call if prompted. Expand it and confirm it used
sample-mcpand returned5. A plausible chat answer without a tool call is not a successful connection test.
Verify with the sample client
For a repeatable protocol check without an editor:
Set MCP_URL to <returned-app-origin>/mcp, keep APP_API_TOKEN in the client environment, and run node client.mjs.
The client refuses non-loopback plaintext HTTP and redirects.
For another MCP client, select its Streamable HTTP transport and configure the endpoint and app credential; qualify that client's protocol and authentication behavior independently.
Avoid putting credentials in client configuration committed to source control.
Deployed-app authentication describes platform sign-in and customer Entra bearer audiences, separately from the sample's shared token.
When both layers use bearer credentials, supply the platform access token in X-Embr-Authorization and the app token in Authorization.
If the proxy instead consumes Authorization for its authentication, that header is removed before forwarding.
A browser sign-in redirect is not an MCP OAuth flow.
The sample is not a multi-user OAuth authorization server.
If platform bearer authentication is enabled, add the separately acquired platform token as X-Embr-Authorization in your client's headers.
Use a credential reference, not a literal token.
Then repeat tool discovery and invocation; do not treat an interactive browser sign-in as successful MCP authentication.
Keep required authentication enabled rather than bypassing it to obtain a successful call.
Treat model-supplied arguments and tool output as untrusted. Before extending the sample, enforce caller scopes/roles and resource ownership inside every tool handler. Read-only annotations are client hints, not authorization. Do not add autonomous writes or side-effect tools without an explicit permission and confirmation design.
Troubleshooting
| Result | Check |
|---|---|
503 from /mcp | Set an app token of at least 32 characters and deploy the captured configuration |
401 from /mcp | Supply the exact sample bearer token and verify platform credential forwarding separately |
403 | A supplied Origin must match the exact configured origin; forwarding headers do not establish authorization |
413 | Reduce the JSON request below 8192 bytes |
405 for GET/session termination | Use POST request/response operation; this sample does not offer a GET event stream or sessions |
Clean up
Stop local servers, clear client environment credentials, and revoke the test credential.
Delete only the test app you created when you no longer need it. App deletion does not remove your GitHub repository, resource group, or separately owned services and user-assigned identities.
- Portal
- Builder CLI
Return to the app inventory and choose Delete from the test app's actions. Alternatively, open the app's Settings and select Delete under Danger zone. Confirm the selected app and follow deletion to completion. Deleting from Settings returns you to the home inventory after the deletion request is accepted.
builder app delete --name "<app-name>" \
--subscription "<subscription-id>" --resource-group "<resource-group>"
The command asks for confirmation and waits for deletion unless --no-wait is supplied.
If you choose not to wait, track the returned operation before treating cleanup as complete.
Before sharing the app, verify startup, health, authentication, and a real remote client call in your preview environment. Use host an AI agent when you need an HTTP assistant that calls a separately owned model service; MCP instead exposes tools to a client.