Skip to main content

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-server sample with the official MCP TypeScript SDK v2 and Node 24.
  • Serve stateless Streamable HTTP JSON responses at /mcp and 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​

ItemSample contract
POST /mcpStateless Streamable HTTP JSON-RPC requests with JSON responses
text_lengthRead-only count of Unicode code points; at most 1000 UTF-16 code units of input; no file, network, or state operations
GET /healthPublic process liveness: status 200 and OK body
GET /Public sample identifier
GET or DELETE /mcpStatus 405; no session/event-stream lifecycle
APP_API_TOKENRequired app-side bearer credential, at least 32 characters
PUBLIC_ORIGINOptional exact HTTPS origin without a trailing slash, allowed when a client supplies Origin
MCP_URLClient-side endpoint; defaults to http://127.0.0.1:8080/mcp
PLATFORM_ACCESS_TOKENOptional 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.

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.

  1. Sign in to the Portal with the Microsoft account authorized for your subscription.
  2. Select the subscription and choose Create.
  3. 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.
  4. 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.
  5. 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.
  6. On Review and create, confirm the app, repository, branch, subscription, resource group, and app region. Select Create and deploy.
  7. 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.

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​

  1. Open a project in VS Code with Copilot enabled. The remote server does not need to be cloned into this project.

  2. Provide APP_API_TOKEN securely 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.

  3. 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, use mcpServers and ${APP_API_TOKEN} as described in the linked reference; do not mix the formats.

  4. Start the server connection using your client's MCP controls and review the trust prompt. Confirm that text_length appears in the available tools.

  5. 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-mcp and returned 5. 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​

ResultCheck
503 from /mcpSet an app token of at least 32 characters and deploy the captured configuration
401 from /mcpSupply the exact sample bearer token and verify platform credential forwarding separately
403A supplied Origin must match the exact configured origin; forwarding headers do not establish authorization
413Reduce the JSON request below 8192 bytes
405 for GET/session terminationUse 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.

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.

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.