Construindo um marketplace de plug-ins para agentes
Loja centralizada de ferramentas: publicação, avaliação e distribuição de plug-ins de agentes.
Construindo um marketplace de plug-ins para agentes é uma aula grátis de AI Agents no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Agents, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Agents inclui 4 aulas no total.
O que é um mercado de extensões para agentes?
Um mercado de extensões é um registro centralizado no qual os desenvolvedores publicam ferramentas compartilháveis, outros desenvolvedores as descobrem por meio de pesquisas e os sistemas de agentes as instalam automaticamente. Pense nele como o npm para ferramentas de agentes: publique uma vez e use em qualquer lugar.
Arquitetura do mercado
O mercado tem quatro serviços principais: Registro (armazena os metadados das extensões em um banco de dados), Armazenamento (armazena os arquivos compactados das extensões em um armazenamento de objetos), Pesquisa (índice de texto completo) e Segurança (assina e verifica os pacotes). Tudo é disponibilizado por meio de uma API REST.
# Marketplace REST API endpoints:
API_ROUTES = {
'POST /plugins': 'Publish a new plugin version',
'GET /plugins': 'List all plugins (paginated)',
'GET /plugins/search?q=weather': 'Search by keyword',
'GET /plugins/{name}': 'Get plugin metadata',
'GET /plugins/{name}/{version}': 'Get specific version metadata',
'GET /plugins/{name}/{version}/download': 'Download plugin archive',
'POST /plugins/{name}/{version}/reviews': 'Submit a rating/review',
'GET /plugins/{name}/reviews': 'Get all reviews',
'DELETE /plugins/{name}/{version}': 'Yank (hide) a version',
}
for route, description in API_ROUTES.items():
print(f'{route}: {description}')Publicação de uma extensão
O ponto de acesso de publicação valida o arquivo compactado da extensão, verifica o manifesto, confirma a identidade do publicador (por meio de uma chave de API), procura padrões maliciosos conhecidos, assina o pacote e o armazena. A publicação é atômica: ou o carregamento completo é bem-sucedido, ou nada é armazenado.
import hashlib
import hmac
import os
def publish_plugin(
archive_path: str,
api_key: str,
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
import requests
# Compute checksum for integrity
with open(archive_path, 'rb') as f:
data = f.read()
sha256 = hashlib.sha256(data).hexdigest()
# Sign the archive with the developer's API key
signature = hmac.new(
api_key.encode(), data, hashlib.sha256
).hexdigest()
response = requests.post(
f'{registry_url}/plugins',
files={'archive': (os.path.basename(archive_path), data, 'application/zip')},
headers={
'X-API-Key': api_key,
'X-Signature': signature,
'X-Checksum-SHA256': sha256
},
timeout=60
)
response.raise_for_status()
return response.json()Validação no servidor durante a publicação
O servidor de registro valida cada envio: o manifesto pode ser analisado e está completo, a versão ainda não existe, o arquivo compactado não excede o limite de tamanho e uma verificação básica de segurança é aprovada. Somente depois de todas as verificações o servidor aceita o carregamento.
import zipfile
import io
import json
MAX_ARCHIVE_SIZE_MB = 50
def validate_plugin_archive(archive_bytes: bytes) -> dict:
errors = []
# Size check
size_mb = len(archive_bytes) / 1024 / 1024
if size_mb > MAX_ARCHIVE_SIZE_MB:
errors.append(f'Archive too large: {size_mb:.1f}MB (max {MAX_ARCHIVE_SIZE_MB}MB)')
return {'valid': False, 'errors': errors}
# Must be a valid zip
try:
zf = zipfile.ZipFile(io.BytesIO(archive_bytes))
except zipfile.BadZipFile:
errors.append('Not a valid zip archive')
return {'valid': False, 'errors': errors}
file_list = zf.namelist()
# Must contain plugin.json
if 'plugin.json' not in file_list:
errors.append('Missing plugin.json in archive root')
# Basic security scan: no .pyc files, no hidden dirs
suspicious = [f for f in file_list if f.startswith('.') or '__pycache__' in f]
if suspicious:
errors.append(f'Suspicious files: {suspicious[:3]}')
return {'valid': len(errors) == 0, 'errors': errors, 'files': len(file_list)}
if __name__ == '__main__':
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w') as zf:
zf.writestr('plugin.json', '{"name": "demo-plugin"}')
archive_bytes = buf.getvalue()
print('Validation result:', validate_plugin_archive(archive_bytes))
Assinatura e verificação de pacotes
Assine todos os pacotes publicados com a chave do publicador. Os clientes verificam a assinatura antes da instalação para garantir que o pacote não tenha sido adulterado durante o trânsito ou enquanto estava armazenado.
import hashlib
import hmac
def sign_package(archive_bytes: bytes, secret_key: str) -> str:
return hmac.new(
secret_key.encode(), archive_bytes, hashlib.sha256
).hexdigest()
def verify_package(
archive_bytes: bytes,
signature: str,
secret_key: str
) -> bool:
expected = sign_package(archive_bytes, secret_key)
return hmac.compare_digest(expected, signature)
# Example:
package_data = b'fake plugin archive bytes'
developer_key = 'developer_secret_key_abc123'
sig = sign_package(package_data, developer_key)
print(f'Signature: {sig[:20]}...')
# Verify (registry or installer):
valid = verify_package(package_data, sig, developer_key)
print(f'Signature valid: {valid}')
# Tampered data:
tampered = b'tampered bytes'
print(f'Tampered valid: {verify_package(tampered, sig, developer_key)}')Descoberta de extensões por meio de pesquisa
A API search aceita consultas de texto livre e filtros de etiquetas. Ela retorna resultados classificados usando uma pontuação ponderada: a correspondência exata do nome fica em primeiro lugar, seguida pelas correspondências de etiquetas e, depois, pelas correspondências de palavras-chave da descrição.
import requests
def search_marketplace(
query: str,
tags: list = None,
page: int = 0,
per_page: int = 20,
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
params = {
'q': query,
'page': page,
'per_page': per_page
}
if tags:
params['tags'] = ','.join(tags)
response = requests.get(
f'{registry_url}/plugins/search',
params=params,
timeout=10
)
response.raise_for_status()
return response.json()
# Usage:
results = search_marketplace('weather forecast', tags=['iot', 'weather'])
# {
# 'total': 12,
# 'results': [
# {'name': 'weather-tools', 'version': '2.1.0', 'score': 0.98, ...},
# ...
# ]
# }
print(f'Total results: {results.get("total", 0)}')Instalação de uma extensão
O processo de instalação consiste em baixar o arquivo compactado, verificar a assinatura, extraí-lo para o diretório de extensões, validar o manifesto, verificar as dependências e registrá-lo no registro local de ferramentas. Todas as etapas devem ser concluídas com sucesso ou a instalação será revertida.
import requests
import zipfile
import io
import shutil
import os
def install_plugin(
plugin_name: str,
version: str = 'latest',
plugins_dir: str = 'plugins',
registry_url: str = 'https://registry.agenttools.io'
) -> bool:
# Step 1: Get metadata including expected signature
meta = requests.get(
f'{registry_url}/plugins/{plugin_name}/{version}',
timeout=10
).json()
# Step 2: Download archive
archive_resp = requests.get(
meta['download_url'], timeout=60
)
archive_bytes = archive_resp.content
# Step 3: Verify signature
if not verify_package(archive_bytes, meta['signature'], meta['public_key']):
print('Signature verification FAILED — aborting install')
return False
# Step 4: Extract
install_path = os.path.join(plugins_dir, plugin_name)
if os.path.exists(install_path):
shutil.rmtree(install_path) # remove old version
with zipfile.ZipFile(io.BytesIO(archive_bytes)) as zf:
zf.extractall(install_path)
print(f'Installed {plugin_name} v{version} to {install_path}')
return TrueSistema de avaliações e análises
Um sistema de avaliações ajuda os desenvolvedores a escolher extensões de alta qualidade. Implemente avaliações de 1 a 5 estrelas, com análises textuais opcionais. Os dados agregados das avaliações (mean, quantidade) são armazenados no registro e retornados nos resultados da pesquisa.
import requests
def submit_review(
plugin_name: str,
stars: int,
review_text: str = '',
api_key: str = '',
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
if not 1 <= stars <= 5:
raise ValueError('Stars must be 1-5')
payload = {
'stars': stars,
'review': review_text[:500] # truncate to max length
}
response = requests.post(
f'{registry_url}/plugins/{plugin_name}/reviews',
json=payload,
headers={'X-API-Key': api_key},
timeout=10
)
response.raise_for_status()
return response.json()
def get_reviews(plugin_name: str, registry_url: str = 'https://registry.agenttools.io') -> dict:
response = requests.get(
f'{registry_url}/plugins/{plugin_name}/reviews',
timeout=10
)
return response.json()
# Example response structure:
# {'average_stars': 4.3, 'total_reviews': 47,
# 'reviews': [{'stars': 5, 'review': 'Works great!', 'date': '...'}]}
print('Review system ready')Retirada (descontinuação) de uma versão
Se uma versão publicada tiver um erro crítico ou uma vulnerabilidade de segurança, retire-a: marque-a como oculta no registro para que novas instalações não possam utilizá-la, mas as instalações existentes continuem funcionando. Isso é mais seguro do que excluí-la, pois a exclusão interromperia o funcionamento dos agentes existentes.
import requests
def yank_version(
plugin_name: str,
version: str,
reason: str,
api_key: str,
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
response = requests.delete(
f'{registry_url}/plugins/{plugin_name}/{version}',
json={'reason': reason},
headers={'X-API-Key': api_key},
timeout=10
)
response.raise_for_status()
return response.json()
# Yanked versions:
# - Still downloadable by users who have them pinned
# - Not returned in 'latest' queries
# - Search results show a 'yanked' badge with reason
# - Install of yanked version requires explicit --allow-yanked flag
print('Yanking removes a version from new installs without breaking existing ones.')
print('Always provide a reason: "Security vulnerability in API key handling"')Ferramenta CLI do mercado
Uma interface de linha de comandos facilita o uso do mercado pelo terminal ou por fluxos de integração contínua. Implemente os comandos marketplace install, marketplace search, marketplace publish e marketplace list.
import argparse
import sys
def main():
parser = argparse.ArgumentParser(description='Agent Tool Marketplace CLI')
subparsers = parser.add_subparsers(dest='command')
# Search
search_parser = subparsers.add_parser('search', help='Search plugins')
search_parser.add_argument('query', help='Search query')
search_parser.add_argument('--tag', action='append', dest='tags')
# Install
install_parser = subparsers.add_parser('install', help='Install a plugin')
install_parser.add_argument('plugin', help='Plugin name')
install_parser.add_argument('--version', default='latest')
install_parser.add_argument('--plugins-dir', default='plugins')
# Publish
publish_parser = subparsers.add_parser('publish', help='Publish plugin')
publish_parser.add_argument('archive', help='Path to .zip archive')
publish_parser.add_argument('--api-key', required=True)
args = parser.parse_args()
if args.command == 'search':
results = search_marketplace(args.query, tags=args.tags or [])
for r in results.get('results', []):
print(f'{r["name"]} v{r["version"]} ({r.get("average_stars", "?")} stars)')
elif args.command == 'install':
install_plugin(args.plugin, args.version, args.plugins_dir)
elif args.command == 'publish':
result = publish_plugin(args.archive, args.api_key)
print('Published:', result)Governança do mercado
Um mercado público precisa de regras de governança: organização por espaços de nomes (para evitar a apropriação indevida de nomes), moderação de conteúdo (sem código malicioso), verificação de segurança (análise estática durante o carregamento) e um processo de remoção (para incidentes jurídicos ou de segurança). Essas são preocupações tanto organizacionais quanto técnicas.
MARKETPLACE_POLICIES = {
'namespace_policy': (
'Plugin names must be unique across the registry. '
'Names are claimed on first publish. '
'Transfers require proof of original authorship.'
),
'security_scanning': (
'All uploads are scanned with bandit (Python security linter). '
'Critical vulnerabilities block publish. '
'High vulnerabilities generate a warning visible to installers.'
),
'rate_limits': {
'publish_per_hour': 10,
'search_per_minute': 60,
'install_per_minute': 30
},
'takedown_process': (
'Maintainers can yank versions at any time. '
'Legal/security takedowns processed within 48 hours. '
'Appeals via security@agenttools.io'
)
}
for policy, detail in MARKETPLACE_POLICIES.items():
if isinstance(detail, str):
print(f'{policy}: {detail[:80]}...')
else:
print(f'{policy}: {detail}')Verificação de conhecimentos
Qual é a diferença entre retirar uma versão de extensão e excluí-la?
Recapitulação: criação de um mercado de extensões para agentes
Parabéns por concluir esta lição! Pontos principais:
- Fluxo de publicação: validar o arquivo compactado → verificar a assinatura → realizar a verificação → armazenar e indexar
- Assinatura de pacotes: a assinatura HMAC garante a integridade; os clientes verificam antes da instalação
- Pesquisa: pesquisa de texto completo com filtragem por etiquetas e classificação por relevância
- Instalação: baixar → verificar → extrair → validar → registrar (reversão em caso de falha)
- Avaliações: de 1 a 5 estrelas, com análises textuais agregadas no registro
- Retirada: ocultar das novas instalações sem interromper as existentes
Curso final: O caminho para sistemas autônomos.
Perguntas Frequentes
A aula “Construindo um marketplace de plug-ins para agentes” é grátis?
Sim — o texto completo de “Construindo um marketplace de plug-ins para agentes” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Agents, atualize para CoddyKit PRO. O curso de AI Agents inclui 4 aulas no total.
O que vou aprender em “Construindo um marketplace de plug-ins para agentes”?
Loja centralizada de ferramentas: publicação, avaliação e distribuição de plug-ins de agentes. Você pratica AI Agents com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Agents?
Nenhuma experiência prévia é necessária. AI Agents no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Construindo um marketplace de plug-ins para agentes”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Agents?
Sim. Cada aula de AI Agents inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Projetando ferramentas de agentes reutilizáveis
- Descoberta e registro de plug-ins
- Versionamento e compatibilidade de ferramentas
- Construindo um marketplace de plug-ins para agentes