Build an MCP server from the Orion Python template¶
Draft outline: not validated
Drafted with AI from official sources on 16 Oct 2026. Do not use on a live system until it is written, dry-run by someone other than the author and signed off (planned 2026-12-15). Author: Sandeep Velu · dry-run: Yasar · sign-off: Sivaraj. Work items: AGT-10-T01, AGT-10-T02, AGT-10-T03, AGT-10-T04.
1. Summary¶
When a client system needs to be exposed as tools to more than one agent or copilot, or an existing custom tool is being promoted to the shared catalog.
Risk: Medium (a badly scoped tool exposes client data to every connected agent). Typical duration: 1-2 days per server.
2. When to use and when not to¶
Use it when: When a client system needs to be exposed as tools to more than one agent or copilot, or an existing custom tool is being promoted to the shared catalog.
Do not use it when: to be written by the author.
3. What you must know first¶
- Protocol revision 2026-07-28 is stateless: no initialize handshake and no Mcp-Session-Id; cross-call state uses explicit handles
- Python SDK v2 renamed FastMCP to MCPServer (mcp.server.mcpserver) and moved transport options to run()
- HTTP servers acting as OAuth resource servers must validate token audience and must not pass tokens through to other services
- Return input validation failures as tool execution errors so the model can correct itself
- Tool names, descriptions and schemas are the contract every agent depends on
4. Prerequisites and access¶
- Python 3.10+ and the MCP Python SDK pinned to a major version (mcp>=2,<3 for new servers)
- API contract and test credentials for the target system in Key Vault
- Entra app registration for the server as an OAuth resource
- Container registry and Azure Container Apps environment
5. Steps¶
Outline of the steps; the author adds the commands and a check after each one.
- Copy the template repository and set the server name and owner.
- Define each tool with typed parameters, a clear docstring and read or write tagging.
- Validate inputs and return plain-language errors.
- Read downstream credentials from Key Vault through managed identity, never from code.
- Add OAuth token validation for the Streamable HTTP endpoint, or front the server with API Management.
- Add OpenTelemetry tracing with tool name, duration and error attributes.
- Write contract tests that list tools and call each one against a mock backend.
- Build the container and deploy to Container Apps in the sandbox.
- Connect a test agent and confirm it selects and calls the tools correctly.
- Register the server in the catalog (PB-AGT-10-05).
Draft snippet: Minimal MCP server with a validated read-only tool
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("orion-invoice-tools")
@mcp.tool()
def get_invoice_status(invoice_id: str) -> dict:
"""Return the payment status of one invoice. Read-only. Use for status questions only."""
if not invoice_id.isalnum():
raise ValueError("invoice_id must be letters and digits only")
return {"invoice_id": invoice_id, "status": "paid"} # replace with the ERP call
if __name__ == "__main__":
# Put OAuth validation or API Management in front before exposing beyond the sandbox
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
Snippets to add:
snippets/server/main.py: Minimal MCP server with one read-only tool (SDK v2) (python)snippets/deploy/containerapp.yaml: Container Apps deployment settings (yaml)snippets/tests/test_contract.py: Contract tests for tool list and calls (python)
6. Verify it works¶
A second engineer builds a two-tool server from the template in the sandbox, deploys it and connects a test agent. Pass: contract tests pass, an unauthenticated call is rejected, and the agent calls the right tool for five scripted prompts.
7. Known issues and fixes¶
| Symptom | Likely cause | Fix |
|---|---|---|
| ModuleNotFoundError for mcp.server.fastmcp | SDK v2 installed with v1 imports | Import MCPServer from mcp.server.mcpserver or pin mcp<2 until migrated |
| Requests return 421 on a real hostname | DNS rebinding protection defaults on for localhost hosts in v2 | Configure transport_security for the real host names |
| Agents pick the wrong tool | Vague tool names or descriptions | Rename tools to verb_object and describe when not to use them |
| Server works locally but returns 401 in Azure | Token audience does not match the server's resource URI | Align the app registration identifier with the canonical server URI |
8. Monitoring and alerts¶
| Signal | Alert when | Action |
|---|---|---|
| Tool error rate per tool | Above 5% for 10 minutes | Check the downstream system and recent deploys |
| 401 and 403 responses on the MCP endpoint | Sudden spike | Check token audience, scopes and credential expiry |
9. AWS and open-source equivalents¶
On AWS, AgentCore Gateway can turn APIs and Lambda functions into MCP tools with policy enforcement. Open-source servers use the same MCP SDKs and run on any container platform.
10. Validation record¶
Pending. Planned dry-run scenario: A second engineer builds a two-tool server from the template in the sandbox, deploys it and connects a test agent. Pass: contract tests pass, an unauthenticated call is rejected, and the agent calls the right tool for five scripted prompts.