├── img ├── .gitkeep ├── a2a_mcp.png └── a2a_mcp_readme.png ├── agent_api_template └── python_a2a_template │ ├── __init__.py │ ├── .python-version │ ├── utils │ ├── logging_util.py │ ├── setting_util.py │ ├── util.py │ ├── jwt_util.py │ └── middleware.py │ ├── requirements.txt │ ├── static │ └── agent.json │ ├── router │ ├── __init__.py │ ├── agent_card_router.py │ └── task_router.py │ ├── app.py │ ├── README-zh.md │ ├── service │ ├── custom_task_manager.py │ ├── server.py │ └── task_manager.py │ ├── README-en.md │ └── models │ └── types.py ├── LICENSE ├── CONTRIBUTING.md ├── .gitignore └── README.md /img/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/.python-version: -------------------------------------------------------------------------------- 1 | 3.10 -------------------------------------------------------------------------------- /img/a2a_mcp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetMindAI-Open/Awesome-Agent2Agent/HEAD/img/a2a_mcp.png -------------------------------------------------------------------------------- /img/a2a_mcp_readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetMindAI-Open/Awesome-Agent2Agent/HEAD/img/a2a_mcp_readme.png -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/utils/logging_util.py: -------------------------------------------------------------------------------- 1 | 2 | import logging 3 | 4 | logger = logging.getLogger(__name__) 5 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi==0.115.12 2 | pydantic==2.11.3 3 | python-dotenv==1.1.0 4 | python-jose==3.4.0 5 | sse-starlette==2.2.1 6 | starlette==0.46.2 7 | uvicorn==0.34.1 -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/static/agent.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xxx", 3 | "description": "xxx", 4 | "url": "http://host:port", 5 | "version": "0.0.1", 6 | "capabilities": { 7 | "streaming": false 8 | }, 9 | "skills": [ 10 | { 11 | "id": "xxxx", 12 | "name": "xxx" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/router/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import fastapi 3 | 4 | from router.agent_card_router import agent_card_router 5 | from router.task_router import task_router 6 | 7 | 8 | api_endpoint_router = fastapi.APIRouter() 9 | 10 | api_endpoint_router.include_router(router=agent_card_router) 11 | api_endpoint_router.include_router(router=task_router) 12 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/app.py: -------------------------------------------------------------------------------- 1 | 2 | import asyncio 3 | import click 4 | 5 | from utils.setting_util import setting 6 | from service.custom_task_manager import CustomTaskManager 7 | from service.server import A2AServer 8 | 9 | 10 | @click.command() 11 | @click.option("--host", default="localhost") 12 | @click.option("--port", default=10001) 13 | def main(host, port): 14 | 15 | server = A2AServer( 16 | host=host, 17 | port=port, 18 | ) 19 | server.start() 20 | 21 | 22 | if __name__ == "__main__": 23 | main() 24 | 25 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/README-zh.md: -------------------------------------------------------------------------------- 1 | ### 项目来源说明 2 | 3 | 本项目基于 [google-A2A](https://github.com/google/A2A) 开发。 4 | 5 | 许可证:本项目采用 [Apache 许可证 2.0](https://opensource.org/licenses/Apache-2.0) - 详细内容请查看 [LICENSE](https://github.com/google/A2A/blob/main/LICENSE) 文件。 6 | 7 | ### 开发说明 8 | 1. 必须根据 models/types.py:AgentCard 完善 static/agent.json 9 | 2. 必须在 service/custom_task_manager.py 中完善你的 agent 功能 10 | 3. 如果你的 api 需要 auth 验证,可以在 utils/jwt_util.py:verify_jwt 中完善逻辑 11 | 4. 另外你还可以在 router 中自定义 api 12 | 13 | ### 运行说明 14 | 1. pip3 install -r requirements.txt 15 | 2. python3 app.py 16 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/service/custom_task_manager.py: -------------------------------------------------------------------------------- 1 | 2 | from models.types import ( 3 | SendTaskRequest, 4 | SendTaskStreamingRequest, 5 | ) 6 | from service.task_manager import InMemoryTaskManager 7 | 8 | 9 | class CustomTaskManager(InMemoryTaskManager): 10 | """ TODO 11 | 自定义任务管理器 12 | 13 | custom task manager 14 | """ 15 | 16 | async def on_send_task(self, request: SendTaskRequest): 17 | pass 18 | 19 | async def on_send_task_subscribe( 20 | self, request: SendTaskStreamingRequest 21 | ): 22 | pass 23 | 24 | 25 | custom_task_manager = CustomTaskManager() 26 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/router/agent_card_router.py: -------------------------------------------------------------------------------- 1 | 2 | import fastapi 3 | from fastapi.requests import Request 4 | from fastapi.responses import JSONResponse 5 | from typing import Annotated, Any 6 | 7 | from utils.setting_util import setting 8 | from utils.jwt_util import verify_jwt 9 | 10 | agent_card_router = fastapi.APIRouter(prefix="/.well-known") 11 | 12 | 13 | @agent_card_router.get( 14 | path="/agent.json" 15 | ) 16 | async def get_agent_card( 17 | request: Request, 18 | _: Annotated[Any, fastapi.Depends(verify_jwt)] 19 | ) -> JSONResponse: 20 | return JSONResponse(setting.AgentCARD.model_dump(exclude_none=True)) 21 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/utils/setting_util.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import json 4 | from dotenv import load_dotenv 5 | 6 | from models.types import AgentCard 7 | 8 | load_dotenv() 9 | BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 10 | 11 | 12 | def load_agent_json(): 13 | agent_json_path = os.path.join(BASE_PATH, "static/agent.json") 14 | with open(agent_json_path, "r", encoding="utf-8") as f: 15 | agent_json = json.load(f) 16 | return agent_json 17 | 18 | 19 | class Setting: 20 | 21 | APISecretKey: str = os.getenv("APISecretKey", "your_key") 22 | 23 | AgentCARD: AgentCard = AgentCard(**load_agent_json()) 24 | 25 | 26 | setting = Setting() 27 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/README-en.md: -------------------------------------------------------------------------------- 1 | ## Project Source 2 | 3 | This project is based on [google-A2A](https://github.com/google/A2A). 4 | 5 | License: This project is licensed under the [Apache License 2.0](https://opensource.org/licenses/Apache-2.0) - see the [LICENSE](https://github.com/google/A2A/blob/main/LICENSE) file for details. 6 | 7 | ### Development instructions 8 | 1. You must complete static/agent.json according to models/types.py:AgentCard 9 | 2. You must complete your agent function in service/custom_task_manager.py 10 | 3. If your api requires auth verification, you can complete the logic in utils/jwt_util.py:verify_jwt 11 | 4. You can also customize the api in the router 12 | 13 | ### Running instructions 14 | 1. pip3 install -r requirements.txt 15 | 2. python3 app.py 16 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/utils/util.py: -------------------------------------------------------------------------------- 1 | 2 | from models.types import ( 3 | JSONRPCResponse, 4 | ContentTypeNotSupportedError, 5 | UnsupportedOperationError, 6 | ) 7 | from typing import List 8 | 9 | 10 | def are_modalities_compatible( 11 | server_output_modes: List[str], client_output_modes: List[str] 12 | ): 13 | """Modalities are compatible if they are both non-empty 14 | and there is at least one common element.""" 15 | if client_output_modes is None or len(client_output_modes) == 0: 16 | return True 17 | 18 | if server_output_modes is None or len(server_output_modes) == 0: 19 | return True 20 | 21 | return any(x in server_output_modes for x in client_output_modes) 22 | 23 | 24 | def new_incompatible_types_error(request_id): 25 | return JSONRPCResponse(id=request_id, error=ContentTypeNotSupportedError()) 26 | 27 | 28 | def new_not_implemented_error(request_id): 29 | return JSONRPCResponse(id=request_id, error=UnsupportedOperationError()) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 NetMind.AI 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 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/service/server.py: -------------------------------------------------------------------------------- 1 | 2 | import fastapi 3 | from fastapi.responses import ORJSONResponse 4 | from fastapi.middleware.cors import CORSMiddleware 5 | 6 | from utils.logging_util import logger 7 | from utils.setting_util import setting 8 | from utils.middleware import CustomMiddleware 9 | from router import api_endpoint_router 10 | from service.task_manager import TaskManager 11 | 12 | 13 | class A2AServer: 14 | def __init__( 15 | self, 16 | host="0.0.0.0", 17 | port=5000, 18 | endpoint="/", 19 | ): 20 | self.host = host 21 | self.port = port 22 | self.endpoint = endpoint 23 | self.app = fastapi.FastAPI(default_response_class=ORJSONResponse) 24 | self.app.add_middleware(CustomMiddleware) 25 | self.app.add_middleware( 26 | CORSMiddleware, 27 | allow_origins=["*"], 28 | allow_credentials=True, 29 | allow_methods=["*"], 30 | allow_headers=["*"], 31 | ) 32 | 33 | self.app.include_router(router=api_endpoint_router) 34 | 35 | def start(self): 36 | import uvicorn 37 | 38 | uvicorn.run(self.app, host=self.host, port=self.port) 39 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/utils/jwt_util.py: -------------------------------------------------------------------------------- 1 | 2 | import time 3 | from jose import jwt as jose_jwt 4 | from jose.exceptions import ExpiredSignatureError, JWTError 5 | from typing import Annotated, Any 6 | from collections.abc import AsyncGenerator 7 | from fastapi import Depends, Header, HTTPException 8 | 9 | import sys 10 | sys.path.append('/root/liss_package/Awesome-Agent2Agent/agent_api_template/python_a2a_template') 11 | from utils.setting_util import setting 12 | 13 | 14 | def create_jwt_token() -> str: 15 | data = { 16 | "current_time": int(time.time()), 17 | } 18 | token = jose_jwt.encode( 19 | data, 20 | setting.APISecretKey, 21 | algorithm="HS256", 22 | ) 23 | return token 24 | 25 | 26 | def decode_jwt_token(token: str) -> dict: 27 | data = jose_jwt.decode( 28 | token, 29 | setting.APISecretKey, 30 | algorithms=["HS256"], 31 | ) 32 | return data 33 | 34 | 35 | async def extract_token(token: str | None = Header(default=None)) -> AsyncGenerator[str, Any]: 36 | if not setting.AgentCARD.authentication: 37 | yield None 38 | return 39 | 40 | if not token: 41 | raise HTTPException(status_code=401, detail="Unauthorized, token is required") 42 | 43 | if not token.startswith("Bearer "): 44 | raise HTTPException(status_code=403, detail="Login status is abnormal. Please log in again before proceeding.") 45 | 46 | token = token.split(" ")[1] 47 | yield token 48 | 49 | 50 | async def verify_jwt( 51 | token: Annotated[str, Depends(extract_token)], 52 | ) -> AsyncGenerator[Any]: 53 | if not setting.AgentCARD.authentication: 54 | yield None 55 | return 56 | try: 57 | data = decode_jwt_token(token) 58 | yield data 59 | except JWTError as e: 60 | raise HTTPException(status_code=401, detail="Invalid token") from e 61 | -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/utils/middleware.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | from typing import AsyncIterable, Any 4 | from pydantic import ValidationError 5 | from starlette.requests import Request 6 | from starlette.responses import Response, JSONResponse 7 | from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint, _StreamingResponse 8 | from sse_starlette.sse import EventSourceResponse 9 | 10 | from models.types import ( 11 | JSONRPCResponse, 12 | InvalidRequestError, 13 | JSONParseError, 14 | InternalError, 15 | ) 16 | from utils.logging_util import logger 17 | from utils.setting_util import setting 18 | 19 | 20 | def _handle_exception(e: Exception) -> JSONResponse: 21 | if isinstance(e, json.decoder.JSONDecodeError): 22 | json_rpc_error = JSONParseError() 23 | elif isinstance(e, ValidationError): 24 | json_rpc_error = InvalidRequestError(data=json.loads(e.json())) 25 | else: 26 | logger.error(f"Unhandled exception: {e}") 27 | json_rpc_error = InternalError() 28 | 29 | response = JSONRPCResponse(id=None, error=json_rpc_error) 30 | return JSONResponse(response.model_dump(exclude_none=True), status_code=400) 31 | 32 | 33 | def _create_response(result: Any) -> JSONResponse | EventSourceResponse: 34 | if isinstance(result, AsyncIterable): 35 | 36 | async def event_generator(result) -> AsyncIterable[dict[str, str]]: 37 | async for item in result: 38 | yield {"data": item.model_dump_json(exclude_none=True)} 39 | 40 | return EventSourceResponse(event_generator(result)) 41 | elif isinstance(result, JSONRPCResponse): 42 | return JSONResponse(result.model_dump(exclude_none=True)) 43 | elif isinstance(result, _StreamingResponse): 44 | return result 45 | else: 46 | logger.error(f"Unexpected result type: {type(result)}") 47 | raise ValueError(f"Unexpected result type: {type(result)}") 48 | 49 | 50 | class CustomMiddleware(BaseHTTPMiddleware): 51 | 52 | async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: 53 | # Pre-processing logic 54 | 55 | try: 56 | result = await call_next(request) 57 | return _create_response(result) 58 | except Exception as e: 59 | return _handle_exception(e) 60 | # Post-processing logic 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Thank you for your interest in contributing to Awesome-Agent2Agent! This document provides guidelines and instructions for adding to this curated list. 4 | 5 | ## Adding to the List 6 | 7 | Please ensure your pull request adheres to the following guidelines: 8 | 9 | - Make sure the project/resource is related to the Agent2Agent (A2A) protocol 10 | - Add a single item per pull request 11 | - Check your spelling and grammar 12 | - Make sure your text editor is set to remove trailing whitespace 13 | - The pull request and commit should have a useful title 14 | 15 | ## Project Requirements 16 | 17 | For projects to be included in the list, they should: 18 | 19 | 1. Use or implement the Agent2Agent protocol 20 | 2. Have clear documentation or examples showing how A2A is implemented 21 | 3. Be active or maintained (no abandoned projects) 22 | 23 | ## Format 24 | 25 | When adding a new entry, please use the following format: 26 | 27 | ``` 28 | - [Project Name](URL) - Brief description of the project.   29 | ``` 30 | 31 | For badges, you can use shields.io to create appropriate badges for: 32 | - Programming language 33 | - A2A features (client/server) 34 | - Open/closed source status 35 | - Current version 36 | 37 | ## Templates 38 | 39 | ### Open Source Project 40 | ``` 41 | - [Project Name](https://github.com/username/project) - Brief description of the project.   42 | ``` 43 | 44 | ### Closed Source Product 45 | ``` 46 | - [Product Name](https://product-website.com) - Brief description of how this product implements A2A. 47 | ``` 48 | 49 | ### Resource 50 | ``` 51 | - [Resource Name](URL) - Brief description of the resource. 52 | ``` 53 | 54 | ## Pull Request Process 55 | 56 | 1. Fork the repository 57 | 2. Add your entry in the appropriate section, maintaining alphabetical order within each section 58 | 3. Commit changes to your fork 59 | 4. Submit a pull request with a clear title and description 60 | 5. Address any feedback or changes requested by maintainers 61 | 62 | ## Updating Your Pull Request 63 | 64 | Sometimes a maintainer will ask you to edit your pull request before it is included. This is normally due to spelling errors or because your PR didn't match the list guidelines. Here's what you should do: 65 | 66 | 1. Make the changes directly to your fork of the repository 67 | 2. Push the changes to your fork 68 | 3. The pull request will automatically update 69 | 70 | Thank you for your contributions! -------------------------------------------------------------------------------- /agent_api_template/python_a2a_template/router/task_router.py: -------------------------------------------------------------------------------- 1 | 2 | import fastapi 3 | from fastapi.requests import Request 4 | from typing import Annotated, Any 5 | 6 | from models.types import ( 7 | A2ARequest, 8 | GetTaskRequest, 9 | SendTaskRequest, 10 | SendTaskStreamingRequest, 11 | CancelTaskRequest, 12 | SetTaskPushNotificationRequest, 13 | GetTaskPushNotificationRequest, 14 | TaskResubscriptionRequest, 15 | ) 16 | from utils.logging_util import logger 17 | from utils.jwt_util import verify_jwt 18 | from service.custom_task_manager import custom_task_manager 19 | 20 | 21 | task_router = fastapi.APIRouter(prefix="/tasks") 22 | 23 | 24 | async def _process_request(request: Request): 25 | body = await request.json() 26 | json_rpc_request = A2ARequest.validate_python(body) 27 | 28 | if isinstance(json_rpc_request, GetTaskRequest): 29 | result = await custom_task_manager.on_get_task(json_rpc_request) 30 | elif isinstance(json_rpc_request, SendTaskRequest): 31 | result = await custom_task_manager.on_send_task(json_rpc_request) 32 | elif isinstance(json_rpc_request, SendTaskStreamingRequest): 33 | result = await custom_task_manager.on_send_task_subscribe( 34 | json_rpc_request 35 | ) 36 | elif isinstance(json_rpc_request, CancelTaskRequest): 37 | result = await custom_task_manager.on_cancel_task(json_rpc_request) 38 | elif isinstance(json_rpc_request, SetTaskPushNotificationRequest): 39 | result = await custom_task_manager.on_set_task_push_notification(json_rpc_request) 40 | elif isinstance(json_rpc_request, GetTaskPushNotificationRequest): 41 | result = await custom_task_manager.on_get_task_push_notification(json_rpc_request) 42 | elif isinstance(json_rpc_request, TaskResubscriptionRequest): 43 | result = await custom_task_manager.on_resubscribe_to_task( 44 | json_rpc_request 45 | ) 46 | else: 47 | logger.warning(f"Unexpected request type: {type(json_rpc_request)}") 48 | raise ValueError(f"Unexpected request type: {type(request)}") 49 | 50 | return result 51 | 52 | 53 | @task_router.post( 54 | path="/get" 55 | ) 56 | async def get_task( 57 | request: Request, 58 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 59 | ) -> fastapi.Response: 60 | result = await _process_request(request) 61 | return result 62 | 63 | 64 | @task_router.post( 65 | path="/send" 66 | ) 67 | async def send_task( 68 | request: Request, 69 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 70 | ) -> fastapi.Response: 71 | result = await _process_request(request) 72 | return result 73 | 74 | 75 | @task_router.post( 76 | path="/sendSubscribe" 77 | ) 78 | async def send_task_subscribe( 79 | request: Request, 80 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 81 | ) -> fastapi.Response: 82 | result = await _process_request(request) 83 | return result 84 | 85 | 86 | @task_router.post( 87 | path="/cancel" 88 | ) 89 | async def cancel_task( 90 | request: Request, 91 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 92 | ) -> fastapi.Response: 93 | result = await _process_request(request) 94 | return result 95 | 96 | 97 | @task_router.post( 98 | path="/pushNotification/set" 99 | ) 100 | async def set_task_push_notification( 101 | request: Request, 102 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 103 | ) -> fastapi.Response: 104 | result = await _process_request(request) 105 | return result 106 | 107 | 108 | @task_router.post( 109 | path="/pushNotification/get" 110 | ) 111 | async def get_task_push_notification( 112 | request: Request, 113 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 114 | ) -> fastapi.Response: 115 | result = await _process_request(request) 116 | return result 117 | 118 | 119 | @task_router.post( 120 | path="/resubscribe" 121 | ) 122 | async def resubscribe_to_task( 123 | request: Request, 124 | _: Annotated[Any, fastapi.Depends(verify_jwt)], 125 | ) -> fastapi.Response: 126 | result = await _process_request(request) 127 | return result 128 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | resource/ 3 | 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # UV 102 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | #uv.lock 106 | 107 | # poetry 108 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 109 | # This is especially recommended for binary packages to ensure reproducibility, and is more 110 | # commonly ignored for libraries. 111 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 112 | #poetry.lock 113 | 114 | # pdm 115 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 116 | #pdm.lock 117 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 118 | # in version control. 119 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 120 | .pdm.toml 121 | .pdm-python 122 | .pdm-build/ 123 | 124 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 125 | __pypackages__/ 126 | 127 | # Celery stuff 128 | celerybeat-schedule 129 | celerybeat.pid 130 | 131 | # SageMath parsed files 132 | *.sage.py 133 | 134 | # Environments 135 | .env 136 | .venv 137 | env/ 138 | venv/ 139 | ENV/ 140 | env.bak/ 141 | venv.bak/ 142 | 143 | # Spyder project settings 144 | .spyderproject 145 | .spyproject 146 | 147 | # Rope project settings 148 | .ropeproject 149 | 150 | # mkdocs documentation 151 | /site 152 | 153 | # mypy 154 | .mypy_cache/ 155 | .dmypy.json 156 | dmypy.json 157 | 158 | # Pyre type checker 159 | .pyre/ 160 | 161 | # pytype static type analyzer 162 | .pytype/ 163 | 164 | # Cython debug symbols 165 | cython_debug/ 166 | 167 | # PyCharm 168 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 169 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 170 | # and can be added to the global gitignore or merged into this file. For a more nuclear 171 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 172 | #.idea/ 173 | 174 | # Ruff stuff: 175 | .ruff_cache/ 176 | 177 | # PyPI configuration file 178 | .pypirc 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Awesome-Agent2Agent [](https://awesome.re) 2 | 3 |
The curated list of Agent2Agent (A2A) protocol projects, templates, tools, and resources — enabling a future of interoperable AI agents.
6 |