MicrosoftMicrosoft Certified: Multi-Agent AI Solutions Expert2026-102.3 Build and integrate tool ecosystems
Choose stdio or Streamable HTTP for MCP servers
Completed by the knowledge check
Learning objective
Build MCP servers with discoverable typed tools and select stdio or streamable HTTP transports.
Core idea
A Model Context Protocol (MCP) server exposes operations through discoverable schemas, rather than requiring every client to know a private API in advance. For an architect building a reusable tool service, the server contract has two parts: the tools it advertises and the transport through which clients reach them.
MCP revision 2026-07-28 uses per-request version and capability metadata without an initialization handshake. Revision 2025-11-25 uses a handshake and connection-scoped capabilities. Select a mutually supported revision; the detailed server and transport examples here use 2025-11-25 for compatibility with handshake-based integrations.
Choose stdio for a client-managed local subprocess and Streamable HTTP for an independently running server accessed over HTTP. Neither transport changes the business meaning of a tool. See 2.3.4 for authentication and client-side approval, and 2.3.5 for Azure Functions’ managed tool-trigger implementation.
How it works
Select the protocol revision
For 2026-07-28, servers must implement server/discover. Clients can use it to learn supported versions before invoking operations, or handle UnsupportedProtocolVersionError when a request declares an unsupported version. The response lists supported versions so the client can choose a compatible one. Capabilities travel in request metadata rather than being established by initialize.
Streamable HTTP in 2026-07-28 removes the GET stream endpoint and protocol-level sessions. Its request-scoped operation differs from the GET and MCP-Session-Id rules described here for 2025-11-25. Implement each supported revision deliberately; don’t combine a stateless request with a required legacy session handshake.
Make the tools discoverable
Under MCP 2025-11-25, a server that supports tools declares the tools capability in its initialization response. If it emits notifications when its tool list changes, it advertises tools.listChanged: true. The client discovers definitions with tools/list, which supports pagination through cursor and nextCursor.
A tool definition includes a unique name and an inputSchema. It can also include a human-readable title, a description, an outputSchema, and behavior annotations. Give each operation a distinct name and describe what it does, what input it needs, and whether it changes state. A client can’t reliably distinguish a lookup from a destructive update if both have vague descriptions.
For this protocol revision, JSON Schema defaults to draft 2020-12 when the schema omits $schema. An input schema must be a valid schema object, not null. A no-argument tool can use an object schema with additionalProperties: false to explicitly accept only an empty object.
The following illustrative definition exposes a narrow read operation:
{
"name": "lookup_invoice",
"description": "Read an invoice by identifier.",
"inputSchema": {
"type": "object",
"properties": { "invoice_id": { "type": "string" } },
"required": ["invoice_id"],
"additionalProperties": false
}
}lookup_invoice and invoice_id are application-defined names. This definition belongs in the tool list; it doesn’t itself implement the lookup or establish caller permission.
Implement the invocation and result contract
A client invokes a tool through tools/call with params.name and params.arguments. The server dispatches the approved name to its handler, validates inputs, checks access, and executes the operation. The MCP specification requires servers to validate inputs and implement access controls. Don’t rely on a client model to enforce the advertised schema.
Return unstructured results in content. When a tool returns a JSON object as structured data, use structuredContent; this is server-produced data, not schema-constrained language-model generation. If the tool declares outputSchema, the server must produce structured results conforming to it, and the client should validate them. For compatibility, the specification also recommends returning serialized structured data in a text content block.
Separate protocol errors from execution errors. An unknown tool or malformed protocol request uses a JSON-RPC error. A business operation that fails can return a tool result with isError: true and actionable information. A model can sometimes correct an invalid business argument, but it shouldn’t mistake a failed operation for success merely because a JSON-RPC response arrived.
If the catalog changes and the server advertised listChanged, send notifications/tools/list_changed. The client can then refresh discovery. A notification signals a changed catalog; it doesn’t prove that changed schemas remain compatible with a consumer. See 2.3.2 for contract-version decisions.
Use stdio for a managed local process
With stdio, the client starts the server as a subprocess and exchanges JSON-RPC messages through the process’s standard input and output. Messages are UTF-8, delimited by newlines, and must not contain embedded newlines. The server must write only valid MCP messages to stdout. Send diagnostic logging to stderr so a log line doesn’t corrupt the protocol stream.
This deployment fits tools tied to the client’s local environment, such as an approved repository inspection service. The client controls which executable starts and what credentials and filesystem access it receives. Process-local delivery avoids operating a shared network endpoint, but also ties lifecycle and environment management to each client host.
Microsoft Foundry Agent Service accepts only a remote MCP server endpoint. To reuse a local server with that runtime, self-host it on Azure Container Apps or Azure Functions; a stdio command isn’t a remote connection. Microsoft’s hosting comparison requires HTTP POST/GET endpoints for Container Apps and Streamable HTTP for Functions. For this Foundry integration path, the table labels both hosts “Stateless only”; don’t design it around durable protocol-session state.
For a private Foundry MCP server, Microsoft documents Container Apps with internal-only ingress (--internal-only true) on a dedicated MCP subnet. The same hosting comparison lists custom authentication for Container Apps and key-based access for Functions, with API Management required for OAuth in that Functions integration path. See 2.3.5 for the separate Functions tool-trigger and built-in authentication features.
Use Streamable HTTP for an independent service
Streamable HTTP uses one MCP endpoint, such as /mcp, for HTTP POST and GET. A client sends each JSON-RPC message in a separate POST and advertises acceptance of both application/json and text/event-stream. A request can receive a JSON response or an SSE stream. A GET can open a server message stream; a server that doesn’t offer that stream responds with HTTP 405.
This is different from the separate HTTP+SSE transport defined in protocol version 2024-11-05. Using SSE within Streamable HTTP doesn’t mean you have selected that separate transport. Match the client’s transport implementation to the endpoint rather than choosing from the presence of “SSE” in a log.
For a stateful HTTP session, the server can assign MCP-Session-Id during initialization. Subsequent requests carry that identifier, and HTTP requests after negotiation carry MCP-Protocol-Version. Session handling is transport state, not user authentication. This optional protocol mechanism isn’t a guarantee that Foundry’s documented stateless hosting integrations retain sessions.
Validate the Origin header; an invalid present origin must receive HTTP 403. For local HTTP servers, bind to localhost rather than every network interface. The specification recommends authentication for connections. These controls matter even for a developer-hosted endpoint because a browser-origin request can reach a local network service.
Key distinctions and trade-offs
Discovery versus execution: tools/list describes callable interfaces. tools/call requests execution. A listed tool still needs input validation and access checks.
Input versus output typing: inputSchema describes arguments. outputSchema describes structured results. A successful input check doesn’t validate the observation returned by a backend.
Local process versus independent endpoint: Stdio makes each client responsible for starting and supervising a process. Streamable HTTP centralizes service operation but adds network, authentication, and session considerations.
Choose transport from deployment ownership while preserving narrow tool contracts and validation.
Architecture / decision scenario
A team builds an invoice lookup server used by a local development assistant and a shared hosted assistant. It exposes lookup_invoice with a required identifier and implements authorization inside the handler. The server advertises tools, and clients discover its schema through tools/list before invoking tools/call.
For local development, the assistant launches a stdio subprocess with a constrained environment. Diagnostics go to stderr. For the shared Foundry assistant, the team hosts a Streamable HTTP endpoint on Azure Container Apps with origin validation and custom authentication. It uses the documented stateless hosting design; if private access is required, it selects internal-only ingress on a dedicated MCP subnet. Both deployments use the same business contract and return an explicit error when the caller can’t read an invoice.
An added tool triggers notifications/tools/list_changed only when that capability was advertised. The clients refresh their catalog but still apply their approved-tool policy. The transport allows reuse; it doesn’t make every discovered operation appropriate for both assistants.
Common exam traps
- Returning
nullinstead of an input schema for a no-argument tool. - Writing ordinary logs to a stdio server’s stdout.
- Treating
structuredContentas model structured-output generation. - Returning every business failure as a malformed-protocol error.
- Confusing Streamable HTTP’s optional SSE responses with HTTP+SSE transport.
- Using
MCP-Session-Idas proof of identity or permission.
What to remember
Advertise tools, expose their schemas, validate calls, and distinguish result failures from protocol errors. Choose stdio for a client-managed subprocess and Streamable HTTP for a network service. Apply the lifecycle and transport rules for the negotiated protocol revision.
Knowledge check
A client launches a repository-inspection MCP server as a stdio subprocess. After a debug build added log statements that print to standard output, the client intermittently fails to parse server messages. Which change fixes the failure?
A server using MCP 2025-11-25 exposes a tool that takes no arguments and should accept only an empty argument object. Which inputSchema expresses that contract?
null{ "type": "object" }{ "type": "object", "properties": {} }{ "type": "object", "additionalProperties": false }null . An object schema without additionalProperties: false accepts any object, including one with arguments; rejecting additional properties limits the tool to an empty object.A valid tools/call reaches its known handler, but the business operation fails. How should the server report the failure?
isError: true and the failure detailsA remote MCP 2025-11-25 endpoint accepts POST and GET at one URL, and some POST requests return SSE streams. Which transport interpretation is correct?
2024-11-052026-07-282025-11-25 can return JSON or SSE from POST and can support a GET stream at the same endpoint. SSE in a response does not select the older separate HTTP+SSE transport, and the later revision’s session-free rules must not be mixed into this design.A team’s MCP server runs locally over stdio, and a Microsoft Foundry Agent Service agent must now use its tools. What does the integration require?
MCP-Session-IdReferences
- Microsoft, Connect agents to MCP server endpoints
- Model Context Protocol, Tools
- Model Context Protocol, Transports
- Model Context Protocol, Versioning and compatibility
- Model Context Protocol, Streamable HTTP, revision 2026-07-28
- Model Context Protocol, Lifecycle