Chapter 4 · Build
Repository Anatomy & Python Basics
A map of the project folders, plus the small set of Python ideas you need to read the code with confidence. You don't have to be a Python expert — just comfortable with these six concepts.
🎯 What you'll be able to do
- Navigate the project folders and know what each is responsible for
- Read async functions, context managers, and Pydantic models
- Understand modules, packages, and environment variables
- Know which folders you'll touch first as a beginner
Repository layout
Here is the whole project at a glance. The comments show what each file is for — you'll meet them in detail in the Implementation Walkthrough.
jira-mcp-copilot-studio
|-- app
| |-- server.py # entry point, builds the FastMCP app
| |-- middleware.py # origin / gateway / bearer checks
| |-- config.py # settings from env vars and .env
| |-- context.py # request-scoped token storage
| |-- errors.py # error envelope helpers
| |-- telemetry.py # logging + redaction + App Insights
| |-- auth/bearer.py # token + Jira site resolution
| |-- jira/client.py # async Jira REST client
| |-- jira/trim.py # payload trimming models
| |-- tools/__init__.py # MCP tool registration
|-- docs # implementation, deployment, security docs
|-- infra # main.bicep, containerapp.bicep, apim-policy.xml
|-- openapi # mcp-connector.swagger.json, rest-fallback
|-- scripts # run_local.ps1, deploy.ps1, smoke.py
|-- tests # unit tests
|-- Dockerfile
|-- pyproject.toml
|-- requirements.txt
|-- azure.yamlWhat each area owns
| Area | Responsibility |
|---|---|
app | Runtime server code. |
app/tools | MCP tool definitions. |
app/jira | Jira API client and payload trimming. |
app/auth | Delegated bearer token and Jira site resolution. |
infra | Azure infrastructure (Bicep, APIM policy). |
openapi | Copilot Studio connector definitions. |
scripts | Local run, deployment, and smoke validation scripts. |
tests | Unit tests. |
docs | Production docs and architecture decisions. |
app/tools/__init__.py (the tool menu) and app/config.py (the settings). Leave app/middleware.py and the infra/ Bicep files alone until you've read their chapters — small changes there have big security and deployment consequences.Python ideas you'll use
Module
.py file is a module. app/server.py is imported as app.server.Package
__init__.py is a package. The app folder is a package.Environment variable
$env:MAX_RESPONSE_BYTES = "90000". Read via pydantic-settings in config.py.Async functions
The server uses asynchronous I/O because web servers spend most of their time waiting for network calls. An async function can wait for one operation without blocking the whole server.
async def jira_whoami() -> dict:
...Context manager
The async with block guarantees setup and cleanup happen reliably — here, it opens an HTTP client, resolves the Jira site, and closes the client afterward.
async with JiraClient() as jira:
return await jira.whoami()Pydantic model
Pydantic models define structured data. In app/jira/trim.py, IssueSummary defines the compact issue shape returned to the agent — predictable and small, instead of raw Jira JSON.
class IssueSummary(BaseModel):
id: str | None = None
key: str | None = None
status: str | None = None
summary: str | None = None
assignee: str | None = None
url: str | None = None❓ Concept check
Why does a web server benefit from async functions even though most code in this project isn't doing heavy computation?
📌 Chapter summary
app/holds runtime code;infra/,openapi/,scripts/, andtests/hold deployment, connector, automation, and tests.- You only need six Python ideas: module, package, async functions, context managers, Pydantic models, and environment variables.