azurefunctions-agents-runtime 0.0.0.dev2__tar.gz → 0.0.0.dev6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {azurefunctions_agents_runtime-0.0.0.dev2 → azurefunctions_agents_runtime-0.0.0.dev6}/LICENSE.md +20 -20
- azurefunctions_agents_runtime-0.0.0.dev6/PKG-INFO +504 -0
- azurefunctions_agents_runtime-0.0.0.dev6/README.md +475 -0
- azurefunctions_agents_runtime-0.0.0.dev6/pyproject.toml +109 -0
- {azurefunctions_agents_runtime-0.0.0.dev2 → azurefunctions_agents_runtime-0.0.0.dev6}/setup.cfg +4 -4
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/__init__.py +55 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/_blob_history.py +368 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/_credential.py +79 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/_function_tool.py +97 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/_logger.py +14 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/app.py +95 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/client_manager.py +237 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/__init__.py +76 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/env.py +101 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/loader.py +109 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/merge.py +149 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/paths.py +41 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/schema.py +155 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/config/validation.py +105 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/discovery/__init__.py +0 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/discovery/mcp.py +197 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/discovery/skills.py +102 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/discovery/tools.py +133 -0
- {azurefunctions_agents_runtime-0.0.0.dev2 → azurefunctions_agents_runtime-0.0.0.dev6}/src/azure_functions_agents/public/index.html +1523 -1504
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/registration/__init__.py +13 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/registration/_handlers.py +351 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/registration/_naming.py +84 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/registration/capabilities.py +70 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/registration/endpoints.py +403 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/registration/triggers.py +159 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/runner.py +620 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/system_tools/__init__.py +0 -0
- {azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents → azurefunctions_agents_runtime-0.0.0.dev6/src/azure_functions_agents/system_tools}/sandbox.py +326 -288
- azurefunctions_agents_runtime-0.0.0.dev6/src/azurefunctions_agents_runtime.egg-info/PKG-INFO +504 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azurefunctions_agents_runtime.egg-info/SOURCES.txt +57 -0
- azurefunctions_agents_runtime-0.0.0.dev6/src/azurefunctions_agents_runtime.egg-info/requires.txt +20 -0
- {azurefunctions_agents_runtime-0.0.0.dev2 → azurefunctions_agents_runtime-0.0.0.dev6}/src/azurefunctions_agents_runtime.egg-info/top_level.txt +0 -1
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_app.py +193 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_blob_history.py +487 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_client_manager.py +122 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_env.py +235 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_fixtures.py +487 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_loader.py +421 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_merge.py +301 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_paths.py +52 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_schema.py +81 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_config_validation.py +268 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_discovery_mcp.py +585 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_discovery_skills.py +148 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_discovery_tools.py +121 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_package_imports.py +22 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_registration_capabilities.py +141 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_registration_endpoints.py +410 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_registration_handlers.py +371 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_registration_triggers.py +655 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_runner_streaming.py +148 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_sandbox_session_id.py +119 -0
- azurefunctions_agents_runtime-0.0.0.dev6/tests/test_system_tools_sandbox.py +30 -0
- azurefunctions_agents_runtime-0.0.0.dev2/PKG-INFO +0 -386
- azurefunctions_agents_runtime-0.0.0.dev2/README.md +0 -369
- azurefunctions_agents_runtime-0.0.0.dev2/pyproject.toml +0 -26
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/__init__.py +0 -20
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/app.py +0 -720
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/arm.py +0 -95
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/client_manager.py +0 -84
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/config.py +0 -191
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/connector_tool_cache.py +0 -124
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/connector_tools.py +0 -267
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/connectors.py +0 -460
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/mcp.py +0 -93
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/runner.py +0 -406
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/skills.py +0 -24
- azurefunctions_agents_runtime-0.0.0.dev2/src/azure_functions_agents/tools.py +0 -316
- azurefunctions_agents_runtime-0.0.0.dev2/src/azurefunctions_agents_runtime.egg-info/PKG-INFO +0 -386
- azurefunctions_agents_runtime-0.0.0.dev2/src/azurefunctions_agents_runtime.egg-info/SOURCES.txt +0 -23
- azurefunctions_agents_runtime-0.0.0.dev2/src/azurefunctions_agents_runtime.egg-info/requires.txt +0 -7
- azurefunctions_agents_runtime-0.0.0.dev2/src/copilot_functions/__init__.py +0 -3
- {azurefunctions_agents_runtime-0.0.0.dev2 → azurefunctions_agents_runtime-0.0.0.dev6}/src/azurefunctions_agents_runtime.egg-info/dependency_links.txt +0 -0
{azurefunctions_agents_runtime-0.0.0.dev2 → azurefunctions_agents_runtime-0.0.0.dev6}/LICENSE.md
RENAMED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) Microsoft Corporation.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Microsoft Corporation.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: azurefunctions-agents-runtime
|
|
3
|
+
Version: 0.0.0.dev6
|
|
4
|
+
Summary: A markdown-first programming model for building AI agents on Azure Functions, powered by the Microsoft Agent Framework.
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE.md
|
|
9
|
+
Requires-Dist: azure-functions==2.1.*
|
|
10
|
+
Requires-Dist: agent-framework-core==1.3.*
|
|
11
|
+
Requires-Dist: agent-framework-openai==1.3.*
|
|
12
|
+
Requires-Dist: agent-framework-foundry==1.3.*
|
|
13
|
+
Requires-Dist: pydantic==2.13.*
|
|
14
|
+
Requires-Dist: python-frontmatter==1.1.*
|
|
15
|
+
Requires-Dist: azurefunctions-extensions-http-fastapi==1.0.*
|
|
16
|
+
Requires-Dist: aiohttp==3.13.*
|
|
17
|
+
Requires-Dist: azure-identity==1.25.*
|
|
18
|
+
Requires-Dist: azure-storage-blob==12.28.*
|
|
19
|
+
Requires-Dist: jsonschema==4.26.*
|
|
20
|
+
Requires-Dist: mcp==1.27.*
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: ruff==0.15.*; extra == "dev"
|
|
23
|
+
Requires-Dist: mypy==2.1.*; extra == "dev"
|
|
24
|
+
Requires-Dist: pydantic==2.13.*; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest==9.0.*; extra == "dev"
|
|
26
|
+
Requires-Dist: types-jsonschema==4.26.*; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov==7.1.*; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# azurefunctions-agents-runtime (Preview)
|
|
31
|
+
|
|
32
|
+
> **Public preview.** The features described here are available for preview use and may change before general availability.
|
|
33
|
+
|
|
34
|
+
A markdown-first programming model for building AI agents on Azure Functions, powered by the [Microsoft Agent Framework (MAF)](https://github.com/microsoft/agent-framework).
|
|
35
|
+
|
|
36
|
+
- **Build agents with markdown** — write instructions, configure triggers, and bind tools in `.agent.md` files
|
|
37
|
+
- **Run on any Azure Functions trigger** — trigger agents on timer, queue, blob, HTTP, Event Hub, Service Bus, Cosmos DB, and more
|
|
38
|
+
- **Connect to 1,400+ services** — use connector-backed MCP servers to let agents act through Office 365, Teams, SQL, Salesforce, SAP, and hundreds of other connectors
|
|
39
|
+
- **Extend with MCP servers** — plug in remote HTTP MCP servers, including MCP servers backed by connectors
|
|
40
|
+
- **Build custom tools in plain Python** — drop a `.py` file in `tools/`, decorate functions with `@tool`, and pull in any package you need
|
|
41
|
+
- **Automatic HTTP and MCP endpoints** — optionally expose your agent as an HTTP chat API and MCP server with no extra code
|
|
42
|
+
- **Serverless with built-in session management** — scales to zero, persists multi-turn conversations in Azure Blob Storage
|
|
43
|
+
- **Pluggable model providers** — bring OpenAI, Azure OpenAI, or Microsoft Foundry credentials and the runtime auto-detects the right client
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
The package is published on PyPI as **`azurefunctions-agents-runtime`**.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install azurefunctions-agents-runtime
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Add it to your function app's `requirements.txt`:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
azurefunctions-agents-runtime
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Model Provider Configuration
|
|
60
|
+
|
|
61
|
+
The runtime uses Microsoft Agent Framework, which supports OpenAI, Azure OpenAI, and Microsoft Foundry as inference back-ends. Auto-detection picks the first provider whose env vars are set, in this order:
|
|
62
|
+
|
|
63
|
+
1. `AZURE_OPENAI_ENDPOINT` → Azure OpenAI
|
|
64
|
+
2. `FOUNDRY_PROJECT_ENDPOINT` → Microsoft Foundry
|
|
65
|
+
3. `OPENAI_API_KEY` → OpenAI
|
|
66
|
+
|
|
67
|
+
You can pin the provider explicitly with `MAF_PROVIDER=openai|azure_openai|foundry`.
|
|
68
|
+
|
|
69
|
+
| Provider | Required env vars | Notes |
|
|
70
|
+
| ----------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| OpenAI | `OPENAI_API_KEY`, optional `MAF_MODEL` (default `gpt-4o-mini`) | `MAF_MODEL` applies directly for OpenAI. |
|
|
72
|
+
| Azure OpenAI | `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_DEPLOYMENT`, optional `AZURE_OPENAI_API_VERSION` | `AZURE_OPENAI_DEPLOYMENT` takes precedence over `MAF_MODEL`. If `AZURE_OPENAI_API_KEY` is omitted the SDK uses `DefaultAzureCredential` (AAD); set `AZURE_CLIENT_ID` in multi-identity Function Apps. |
|
|
73
|
+
| Microsoft Foundry | `FOUNDRY_PROJECT_ENDPOINT`, optional `FOUNDRY_MODEL` | `FOUNDRY_MODEL` takes precedence over `MAF_MODEL`. Uses `DefaultAzureCredential`; set `AZURE_CLIENT_ID` in multi-identity Function Apps. |
|
|
74
|
+
|
|
75
|
+
Model resolution precedence is: explicit requested model > provider-specific env (`AZURE_OPENAI_DEPLOYMENT` for Azure OpenAI, `FOUNDRY_MODEL` for Foundry) > `MAF_MODEL` > provider default.
|
|
76
|
+
|
|
77
|
+
## Quick Start
|
|
78
|
+
|
|
79
|
+
### 1. Create the agent file
|
|
80
|
+
|
|
81
|
+
Create `main.agent.md`:
|
|
82
|
+
|
|
83
|
+
```markdown
|
|
84
|
+
---
|
|
85
|
+
name: My Agent
|
|
86
|
+
description: A helpful assistant
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
You are a helpful assistant. Answer questions concisely.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 2. Create the function app entry point
|
|
93
|
+
|
|
94
|
+
Create `function_app.py`:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from azure_functions_agents import create_function_app
|
|
98
|
+
|
|
99
|
+
app = create_function_app()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> The app root is auto-detected from `AzureWebJobsScriptRoot` (set by `func start` and the Azure Functions host). You can override it with `create_function_app(app_root=Path(__file__).parent)` or the `AZURE_FUNCTIONS_AGENTS_APP_ROOT` env var.
|
|
103
|
+
|
|
104
|
+
### 3. Create `host.json`
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"version": "2.0",
|
|
109
|
+
"extensions": {
|
|
110
|
+
"http": {
|
|
111
|
+
"routePrefix": ""
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"extensionBundle": {
|
|
115
|
+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
|
116
|
+
"version": "[4.*, 5.0.0)"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 4. Create `requirements.txt`
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
azurefunctions-agents-runtime
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Connector-backed tools are exposed through MCP servers in `mcp.json`, and connector-triggered apps use the Azure Functions Connector Extension through the Functions extension bundle. No package extra is required.
|
|
128
|
+
|
|
129
|
+
### 5. Set the model provider
|
|
130
|
+
|
|
131
|
+
For local development with OpenAI:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"IsEncrypted": false,
|
|
136
|
+
"Values": {
|
|
137
|
+
"FUNCTIONS_WORKER_RUNTIME": "python",
|
|
138
|
+
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
|
139
|
+
"OPENAI_API_KEY": "sk-...",
|
|
140
|
+
"MAF_MODEL": "gpt-4o-mini"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 6. Start Azurite (local storage emulator)
|
|
146
|
+
|
|
147
|
+
The MCP server endpoint and non-HTTP triggers (timer, queue, blob, etc.) require a storage account. Locally, use [Azurite](https://learn.microsoft.com/azure/storage/common/storage-use-azurite) via Docker:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
docker run -d --name azurite -p 10000:10000 -p 10001:10001 -p 10002:10002 \
|
|
151
|
+
mcr.microsoft.com/azure-storage/azurite \
|
|
152
|
+
azurite --skipApiVersionCheck --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 7. Run locally
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
func start
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Your agent is now running at `http://localhost:7071/` with a built-in chat UI, HTTP API (`/agent/chat`, `/agent/chatstream`), and MCP server (`/runtime/webhooks/mcp`).
|
|
162
|
+
|
|
163
|
+
## Features
|
|
164
|
+
|
|
165
|
+
**Architecture overview:** see [`docs/architecture.md`](docs/architecture.md) for the module map and data flow pipeline.
|
|
166
|
+
|
|
167
|
+
### `main.agent.md`
|
|
168
|
+
|
|
169
|
+
Define an agent with a markdown file. When `main.agent.md` is present, the runtime automatically registers:
|
|
170
|
+
|
|
171
|
+
- **Chat UI** — built-in single-page web interface at the app root
|
|
172
|
+
- **HTTP APIs** — `POST /agent/chat` (JSON) and `POST /agent/chatstream` (SSE)
|
|
173
|
+
- **MCP server** — `/runtime/webhooks/mcp` for VS Code, Claude Desktop, etc.
|
|
174
|
+
- **Session persistence** — multi-turn conversations stored in Azure Blob Storage via the runtime's `BlobHistoryProvider`, reusing the function app's `AzureWebJobsStorage` account
|
|
175
|
+
|
|
176
|
+
Non-main agents can also opt into their own chat UI and HTTP debug endpoints with `debug.chat: true` (or `debug: true`), served at `/agents/{slug}/`, `/agents/{slug}/chat`, and `/agents/{slug}/chatstream`, where `{slug}` is derived from the `.agent.md` filename (not the display `name:` field). See [`docs/front-matter-spec.md#function-name-resolution`](docs/front-matter-spec.md#function-name-resolution).
|
|
177
|
+
|
|
178
|
+
### Event-driven agents (`<name>.agent.md`)
|
|
179
|
+
|
|
180
|
+
Define event-triggered agents with `.agent.md` files. Each file corresponds to a single Azure Function. Supported trigger types:
|
|
181
|
+
|
|
182
|
+
- **Event triggers** — timer, queue, blob, Event Hub, Service Bus, Cosmos DB, Teams, Office 365, etc.
|
|
183
|
+
- **HTTP triggers** — expose agents as REST API endpoints; add `response_example` or `response_schema` for validated JSON responses
|
|
184
|
+
|
|
185
|
+
### Shared capabilities
|
|
186
|
+
- **Markdown-first** — agent instructions, trigger config, and tool bindings in `.agent.md` files
|
|
187
|
+
- **Skills** — progressive-disclosure prompt modules under `skills/<name>/SKILL.md` (loaded on demand via MAF's `SkillsProvider`)
|
|
188
|
+
- **Custom tools** — drop a `.py` file in `tools/`, decorate functions with `@tool`, and they become callable
|
|
189
|
+
- **Connector-backed MCP tools** — call Office 365, Teams, SQL, Salesforce, SAP, and other connectors through HTTP MCP servers
|
|
190
|
+
- **MCP servers** — connect to external remote HTTP MCP servers for additional tools
|
|
191
|
+
- **Sandbox** — Python code execution via Azure Container Apps dynamic sessions; if no explicit sandbox session id is supplied, each invocation gets a fresh GUID-backed session
|
|
192
|
+
|
|
193
|
+
## Agent File Format (`.agent.md`)
|
|
194
|
+
|
|
195
|
+
Agent files use YAML frontmatter + markdown body:
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
---
|
|
199
|
+
name: Agent Name
|
|
200
|
+
description: What this agent does
|
|
201
|
+
|
|
202
|
+
# Optional: system tools (code execution)
|
|
203
|
+
system_tools:
|
|
204
|
+
execute_in_sessions:
|
|
205
|
+
session_pool_management_endpoint: $ACA_SESSION_POOL_ENDPOINT
|
|
206
|
+
|
|
207
|
+
# For triggered agents only (not `main.agent.md`):
|
|
208
|
+
trigger:
|
|
209
|
+
type: timer_trigger # or queue_trigger, connector_trigger, etc.
|
|
210
|
+
args:
|
|
211
|
+
schedule: "0 0 9 * * *" # trigger-specific params passed as kwargs
|
|
212
|
+
|
|
213
|
+
logger: true # optional, default true
|
|
214
|
+
substitute_variables: true # optional, default true — env-var replacement in frontmatter + body
|
|
215
|
+
|
|
216
|
+
# For HTTP-triggered agents: expected response format
|
|
217
|
+
response_example: | # optional — agent returns structured JSON matching this example
|
|
218
|
+
{
|
|
219
|
+
"summary": "A brief summary",
|
|
220
|
+
"keywords": ["keyword1", "keyword2"]
|
|
221
|
+
}
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
Agent instructions in markdown...
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
> **Note**: Earlier preview releases supported a `runtime: copilot|maf` frontmatter field. As of 1.0.0 only Microsoft Agent Framework is used and the field is ignored (with a one-time warning per agent file). Remove it from your `.agent.md` files.
|
|
228
|
+
|
|
229
|
+
### Multiple functions from markdown
|
|
230
|
+
|
|
231
|
+
- **`main.agent.md`** — creates HTTP chat, MCP, and UI endpoints. No other triggers are supported in this file.
|
|
232
|
+
- **`<name>.agent.md`** — creates an event-triggered Azure Function. Exactly one trigger per file. With `debug.chat: true` (or `debug: true`), it also serves `/agents/{slug}/`, `/agents/{slug}/chat`, and `/agents/{slug}/chatstream`. The sanitized filename stem becomes the base Azure Function name. If two agent files sanitize to the same name (for example, `daily-report.agent.md` and `daily_report.agent.md`), the runtime auto-suffixes both the Azure Function name and the non-main debug slug (`_2`, `_3`, ...), keeping them paired in practice (`daily_report_2` ↔ `/agents/daily_report_2/`). The frontmatter `name:` field is display-only. See [`docs/front-matter-spec.md#function-name-resolution`](docs/front-matter-spec.md#function-name-resolution) and [`docs/front-matter-spec.md#debug`](docs/front-matter-spec.md#debug).
|
|
233
|
+
|
|
234
|
+
When a triggered function runs, the agent's markdown body is used as the system instructions. The prompt sent to the agent includes the trigger type and the serialized binding data:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Triggered by: service_bus_queue_trigger
|
|
238
|
+
|
|
239
|
+
Trigger data:
|
|
240
|
+
```json
|
|
241
|
+
{"body": "...", "message_id": "...", ...}
|
|
242
|
+
```
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This applies to all trigger types, including timers (whose data includes fields like `past_due`).
|
|
246
|
+
|
|
247
|
+
For a complete reference of all supported triggers and their parameters, see [docs/triggers.md](docs/triggers.md).
|
|
248
|
+
|
|
249
|
+
### Trigger type resolution
|
|
250
|
+
|
|
251
|
+
| Format | Resolves to | Example |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| `http_trigger` | Runtime HTTP adapter over `app.route(...)` | `http_trigger` |
|
|
254
|
+
| No dots | `app.<type>(...)` | `timer_trigger`, `queue_trigger` |
|
|
255
|
+
| `connector_trigger` | `app.connector_trigger(...)` | `connector_trigger` |
|
|
256
|
+
|
|
257
|
+
### HTTP-triggered agents
|
|
258
|
+
|
|
259
|
+
HTTP-triggered agents expose REST API endpoints that accept JSON input and return structured JSON output. Use `response_example` in the frontmatter to define the expected response format:
|
|
260
|
+
|
|
261
|
+
```yaml
|
|
262
|
+
---
|
|
263
|
+
name: Summarize
|
|
264
|
+
trigger:
|
|
265
|
+
type: http_trigger
|
|
266
|
+
args:
|
|
267
|
+
route: summarize
|
|
268
|
+
methods: ["POST"]
|
|
269
|
+
auth_level: FUNCTION # ANONYMOUS | FUNCTION | ADMIN (default: FUNCTION)
|
|
270
|
+
response_example: |
|
|
271
|
+
{
|
|
272
|
+
"summary": "A brief summary of the content",
|
|
273
|
+
"keywords": ["keyword1", "keyword2"],
|
|
274
|
+
"sentiment": "positive"
|
|
275
|
+
}
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
Analyze the provided content and return a structured summary.
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The agent receives the HTTP request body as input and is instructed to return JSON matching the example. If `response_example` is omitted, the raw agent text is returned as `text/plain`.
|
|
282
|
+
|
|
283
|
+
`response_schema` (JSON Schema) is also supported as an alternative to `response_example` for advanced use cases.
|
|
284
|
+
|
|
285
|
+
### Environment variable substitution
|
|
286
|
+
|
|
287
|
+
`docs/front-matter-spec.md#environment-variable-substitution` is the authoritative reference. In short, the runtime resolves `$VAR` and `%VAR%` placeholders inline in every string value in `agents.config.yaml`, `mcp.json`, agent frontmatter values, and the markdown body (outside fenced code blocks). Missing variables are left as literal placeholders.
|
|
288
|
+
|
|
289
|
+
#### Agent instructions (markdown body)
|
|
290
|
+
|
|
291
|
+
Variable references are resolved inline at load time anywhere string values are supported. Both `$VAR_NAME` and `%VAR_NAME%` syntaxes are supported, where the identifier must match `[A-Za-z_][A-Za-z0-9_]*`:
|
|
292
|
+
|
|
293
|
+
```markdown
|
|
294
|
+
---
|
|
295
|
+
name: Notifier
|
|
296
|
+
description: Sends updates to $TEAM_NAME
|
|
297
|
+
system_tools:
|
|
298
|
+
execute_in_sessions:
|
|
299
|
+
session_pool_management_endpoint: "https://$HOST/api"
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
Send a daily summary email to $TO_EMAIL.
|
|
303
|
+
Post a message to the %TEAM_NAME% team's General channel.
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
If `HOST=contoso.internal`, `TO_EMAIL=alice@example.com`, and `TEAM_NAME=Engineering` are set in the environment, those values resolve inline:
|
|
307
|
+
|
|
308
|
+
> `session_pool_management_endpoint: "https://contoso.internal/api"`
|
|
309
|
+
>
|
|
310
|
+
> Send a daily summary email to alice@example.com.
|
|
311
|
+
>
|
|
312
|
+
> Post a message to the Engineering team's General channel.
|
|
313
|
+
|
|
314
|
+
If a referenced variable is not set, the original `$VAR_NAME` or `%VAR_NAME%` text is left unchanged.
|
|
315
|
+
|
|
316
|
+
The runtime does **not** substitute dictionary keys, `${FOO}` brace syntax, identifiers starting with a digit such as `$9PORT`, or text inside fenced code blocks (`` ``` ``), so documentation examples in your instructions are preserved.
|
|
317
|
+
|
|
318
|
+
For the `$IDENT` syntax, identifiers that include characters outside `[A-Za-z0-9_]` (for example `$VAR-NAME`) are matched greedily up to the first invalid character — so `$VAR-NAME` resolves to `<value-of-VAR>-NAME` when `VAR` is set, and stays `$VAR-NAME` when `VAR` is unset. The `%IDENT%` syntax requires a closing `%` immediately after the identifier, so tokens like `%VAR-NAME%` remain fully literal. Quote or escape the surrounding text if you need a `$IDENT` token to remain literal.
|
|
319
|
+
|
|
320
|
+
To disable substitution for an agent's frontmatter values and markdown body, set `substitute_variables: false` in the frontmatter:
|
|
321
|
+
|
|
322
|
+
```yaml
|
|
323
|
+
---
|
|
324
|
+
name: My Agent
|
|
325
|
+
substitute_variables: false
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
Instructions with literal $VAR references that should not be replaced.
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
> **Note**: `substitute_variables` itself is read before env-var substitution. It must be a literal boolean (`true` or `false`). Setting `substitute_variables: $MY_FLAG` will not be resolved and defaults to `true`.
|
|
332
|
+
|
|
333
|
+
## Custom Python tools
|
|
334
|
+
|
|
335
|
+
Drop a `.py` file in `tools/` and decorate functions with `@tool`. The runtime auto-discovers them at import time and adds them to every agent.
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
# tools/my_tools.py
|
|
339
|
+
from azure_functions_agents import tool
|
|
340
|
+
|
|
341
|
+
@tool
|
|
342
|
+
def reverse_string(text: str) -> str:
|
|
343
|
+
"""Reverse the input string."""
|
|
344
|
+
return text[::-1]
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
`@tool` is re-exported from `agent_framework`. Functions can be sync or async; types in the signature feed MAF's automatic JSON-Schema generation. Tools that need richer schemas can be declared with `agent_framework.FunctionTool` directly.
|
|
348
|
+
|
|
349
|
+
## What `main.agent.md` Enables
|
|
350
|
+
|
|
351
|
+
When a `main.agent.md` file exists in your app root, the runtime automatically registers:
|
|
352
|
+
|
|
353
|
+
### Chat UI
|
|
354
|
+
|
|
355
|
+
A built-in single-page chat interface served at `/` for the main agent, and at `/agents/{slug}/` for any non-main agent with `debug.chat: true` (or `debug: true`). For non-main agents, `{slug}` comes from the `.agent.md` filename after sanitization, not from the display `name:` field. No frontend code needed — just open `http://localhost:7071/` locally or `https://<your-app>.azurewebsites.net/` when deployed. See [`docs/front-matter-spec.md#function-name-resolution`](docs/front-matter-spec.md#function-name-resolution).
|
|
356
|
+
|
|
357
|
+
On first load, you'll be prompted for the base URL and a function key (for deployed apps). These are stored in browser local storage and can be changed via the gear icon.
|
|
358
|
+
|
|
359
|
+
### HTTP Chat API
|
|
360
|
+
|
|
361
|
+
POST endpoints for programmatic access:
|
|
362
|
+
|
|
363
|
+
- **Main agent:** `POST /agent/chat` and `POST /agent/chatstream`
|
|
364
|
+
- **Non-main agent with `debug.chat: true`:** `POST /agents/{slug}/chat` and `POST /agents/{slug}/chatstream` (`{slug}` comes from the `.agent.md` filename after sanitization)
|
|
365
|
+
|
|
366
|
+
The JSON endpoint returns `session_id`, `response`, and `tool_calls`. The streaming endpoint uses Server-Sent Events (SSE) with `session`, `delta`, `intermediate`, `tool_start`, `tool_end`, `done`, and `error` events.
|
|
367
|
+
|
|
368
|
+
Pass `x-ms-session-id` header to continue a conversation across requests. If omitted, a new session is created automatically.
|
|
369
|
+
|
|
370
|
+
### MCP Server
|
|
371
|
+
|
|
372
|
+
An MCP-compatible endpoint at `/runtime/webhooks/mcp` that any MCP client (VS Code, Claude Desktop, etc.) can connect to. Requires the MCP extension system key in the `x-functions-key` header when deployed.
|
|
373
|
+
|
|
374
|
+
### Without `main.agent.md`
|
|
375
|
+
|
|
376
|
+
If there's no `main.agent.md`, the root (`/`) chat UI, `/agent/*` chat APIs, and `/runtime/webhooks/mcp` endpoint are disabled. The app still runs triggered functions, and non-main agents can still opt into per-agent chat surfaces with `debug.chat: true` (or `debug: true`). See [`docs/front-matter-spec.md#debug`](docs/front-matter-spec.md#debug).
|
|
377
|
+
|
|
378
|
+
## MCP Server Configuration
|
|
379
|
+
|
|
380
|
+
You can give your agent access to external MCP servers by creating an `mcp.json` file in the app root. Only remote HTTP MCP servers are supported. The `type` field is optional — when omitted, an entry with a `url` is treated as HTTP. When `type` is specified it must be `"http"` or `"streamable-http"`; any other transport (e.g. `stdio`, `sse`) is rejected with a warning.
|
|
381
|
+
|
|
382
|
+
String values in `mcp.json` support inline environment-variable substitution with both `$VAR` and `%VAR%`. Eligible fields include `url`, `headers` values, `type`, `tools` entries, and Azure identity auth values such as `auth.scope` and `auth.client_id`. Dictionary keys such as server names, environment-variable names, and header names are not substituted.
|
|
383
|
+
|
|
384
|
+
```json
|
|
385
|
+
{
|
|
386
|
+
"servers": {
|
|
387
|
+
"microsoft-learn": {
|
|
388
|
+
"type": "http",
|
|
389
|
+
"url": "https://$MCP_HOST/api",
|
|
390
|
+
"headers": {
|
|
391
|
+
"Authorization": "Bearer $LEARN_MCP_TOKEN"
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"custom-api": {
|
|
395
|
+
"type": "streamable-http",
|
|
396
|
+
"url": "https://example.com/mcp",
|
|
397
|
+
"headers": {
|
|
398
|
+
"Authorization": "Bearer $MCP_TOKEN"
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
"office365-outlook": {
|
|
402
|
+
"type": "http",
|
|
403
|
+
"url": "$O365_MCP_SERVER_URL",
|
|
404
|
+
"tools": ["office365_SendEmailV2"],
|
|
405
|
+
"load_prompts": false,
|
|
406
|
+
"auth": {
|
|
407
|
+
"scope": "https://apihub.azure.com/.default",
|
|
408
|
+
"client_id": "$O365_MCP_CLIENT_ID"
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Tools from configured MCP servers are automatically available to the agent at runtime. Each server entry supports:
|
|
416
|
+
|
|
417
|
+
- **`type`** — optional. When set, must be `"http"` or `"streamable-http"`. When omitted, an entry with a `url` is treated as HTTP.
|
|
418
|
+
- **`url`** — the MCP server endpoint URL (required)
|
|
419
|
+
- **`headers`** — optional HTTP headers (e.g. for authentication)
|
|
420
|
+
- **`tools`** — optional array of tool name patterns to allow (default: `["*"]`)
|
|
421
|
+
- **`load_tools`** — optional boolean controlling whether tools are loaded from the MCP server (default: `true`)
|
|
422
|
+
- **`load_prompts`** — optional boolean controlling whether prompts are loaded from the MCP server (default: `true`). Set this to `false` for MCP servers that do not implement `prompts/list`.
|
|
423
|
+
- **`auth`** — optional Azure Identity authentication configuration. Set `auth.scope` to the token scope required by the MCP server. The runtime uses `DefaultAzureCredential` to acquire the token.
|
|
424
|
+
|
|
425
|
+
By default, MCP auth follows the app-wide identity selection: `AZURE_CLIENT_ID` when set, otherwise the system-assigned identity/default Azure credential chain. To choose a user-assigned managed identity for a single MCP server without changing the app-wide identity, set `auth.client_id` in that server's `mcp.json` entry. If the configured client ID is empty or an unresolved placeholder, the runtime falls back to the app-wide identity selection.
|
|
426
|
+
|
|
427
|
+
> **Note**: Entries without a `url`, with unresolved placeholders in `url`, or with a `type` other than `"http"` / `"streamable-http"`, are ignored with a warning. Use the remote HTTP transport instead.
|
|
428
|
+
|
|
429
|
+
## Session storage
|
|
430
|
+
|
|
431
|
+
Multi-turn conversations are persisted as JSON Lines, one record per message:
|
|
432
|
+
|
|
433
|
+
- **Deployed apps (recommended).** When `AzureWebJobsStorage` is configured —
|
|
434
|
+
as either a connection string or the identity-based
|
|
435
|
+
`AzureWebJobsStorage__blobServiceUri` setting that `azd` provisions —
|
|
436
|
+
history is written to **Azure Blob Storage** via the runtime's
|
|
437
|
+
`BlobHistoryProvider`. One Append Blob per session is stored under
|
|
438
|
+
`agent-sessions/{session_id}.jsonl` inside the
|
|
439
|
+
`azure-functions-agents` container (override with
|
|
440
|
+
`AZURE_FUNCTIONS_AGENTS_SESSION_CONTAINER`). No file share, no storage
|
|
441
|
+
account key, no mount path; the same identity that the function app
|
|
442
|
+
already uses for `AzureWebJobsStorage` reads and writes sessions. In
|
|
443
|
+
multi-identity Function Apps, set `AZURE_CLIENT_ID` so
|
|
444
|
+
`DefaultAzureCredential` selects the intended managed identity.
|
|
445
|
+
- **Local dev fallback.** When neither `AzureWebJobsStorage` nor
|
|
446
|
+
`AzureWebJobsStorage__blobServiceUri` is set, history falls back to MAF's
|
|
447
|
+
`FileHistoryProvider` writing to
|
|
448
|
+
`{AZURE_FUNCTIONS_AGENTS_CONFIG_DIR}/agent-sessions/{session_id}.jsonl`,
|
|
449
|
+
defaulting to `~/.azure-functions-agents/agent-sessions/`.
|
|
450
|
+
|
|
451
|
+
Session ids must match `^[A-Za-z0-9._-]{1,128}$` — anything else is rejected at the API boundary.
|
|
452
|
+
|
|
453
|
+
> **Single-process scope**: A per-session `asyncio.Lock` serializes concurrent turns within a single Function instance. The contract is "one active turn per session id". Multi-instance distributed locking is intentionally out of scope.
|
|
454
|
+
|
|
455
|
+
## Samples
|
|
456
|
+
|
|
457
|
+
See the [`samples/`](samples/) directory for complete, deployable example apps:
|
|
458
|
+
|
|
459
|
+
- [`basic-chat`](samples/basic-chat) — minimal chat agent with sandbox
|
|
460
|
+
- [`daily-azure-report`](samples/daily-azure-report) — timer-triggered agent that emails a daily Azure status report
|
|
461
|
+
- [`daily-tech-news-email`](samples/daily-tech-news-email) — timer-triggered agent that scrapes news and emails a digest
|
|
462
|
+
- [`outlook-reply-agent`](samples/outlook-reply-agent) — connector-triggered agent that drafts replies to incoming Office 365 Outlook email
|
|
463
|
+
|
|
464
|
+
## Deployment Notes
|
|
465
|
+
|
|
466
|
+
### Required Azure App Settings
|
|
467
|
+
|
|
468
|
+
Set the model provider env vars described above (e.g. `OPENAI_API_KEY` and `MAF_MODEL`, `AZURE_OPENAI_ENDPOINT` + `AZURE_OPENAI_DEPLOYMENT`, or `FOUNDRY_PROJECT_ENDPOINT` + `FOUNDRY_MODEL`). For Azure OpenAI and Microsoft Foundry, the provider-specific deployment/model setting takes precedence over `MAF_MODEL`.
|
|
469
|
+
|
|
470
|
+
When the agent uses connector-backed MCP servers, connector triggers, or `execution_sandbox`, the function app's **system-assigned or user-assigned Managed Identity** must be enabled and granted access to the target resource — otherwise `DefaultAzureCredential` will fail to obtain a token. In multi-identity Function Apps, set `AZURE_CLIENT_ID` so the runtime uses the intended managed identity for Azure OpenAI, Foundry, blob-backed session storage, ACA Dynamic Sessions, and ARM/data-plane connector calls. For an individual MCP server, set `auth.client_id` in `mcp.json` to choose a different managed identity just for that server.
|
|
471
|
+
|
|
472
|
+
### Optional config overrides
|
|
473
|
+
|
|
474
|
+
| Setting | Purpose |
|
|
475
|
+
|---|---|
|
|
476
|
+
| `AZURE_FUNCTIONS_AGENTS_APP_ROOT` | Override the app root used to discover `*.agent.md`, `tools/`, `skills/`, and `mcp.json` |
|
|
477
|
+
| `AZURE_FUNCTIONS_AGENTS_CONFIG_DIR` | Override the directory used for session storage |
|
|
478
|
+
| `AGENT_TIMEOUT` | Per-call timeout in seconds (default `900`) |
|
|
479
|
+
| `MAF_PROVIDER` | Pin the model provider (`openai`/`azure_openai`/`foundry`) and skip auto-detection |
|
|
480
|
+
| `MAF_REASONING_EFFORT` | Reasoning effort for supported reasoning models (default `high`; valid values include `none`, `low`, `medium`, `high`, `xhigh`) |
|
|
481
|
+
| `MAF_REASONING_SUMMARY` | Reasoning summary mode for supported reasoning models (default `concise`; valid values are `auto`, `concise`, `detailed`) |
|
|
482
|
+
|
|
483
|
+
## Development
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
# Clone the repo
|
|
487
|
+
git clone https://github.com/Azure/azure-functions-agents-runtime.git
|
|
488
|
+
cd azure-functions-agents-runtime
|
|
489
|
+
|
|
490
|
+
# Install in development mode
|
|
491
|
+
pip install -e .
|
|
492
|
+
|
|
493
|
+
# Build a wheel
|
|
494
|
+
pip install build
|
|
495
|
+
python -m build --wheel
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## Contributing
|
|
499
|
+
|
|
500
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
501
|
+
|
|
502
|
+
## License
|
|
503
|
+
|
|
504
|
+
MIT — see [LICENSE.md](LICENSE.md).
|