@dewtech/dare-cli 2.17.0 → 3.0.0
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.
- package/README.md +98 -3
- package/dist/__tests__/confidence.test.d.ts +2 -0
- package/dist/__tests__/confidence.test.d.ts.map +1 -0
- package/dist/__tests__/confidence.test.js +73 -0
- package/dist/__tests__/confidence.test.js.map +1 -0
- package/dist/__tests__/datamodel.test.d.ts +2 -0
- package/dist/__tests__/datamodel.test.d.ts.map +1 -0
- package/dist/__tests__/datamodel.test.js +131 -0
- package/dist/__tests__/datamodel.test.js.map +1 -0
- package/dist/__tests__/dna-detector.test.d.ts +2 -0
- package/dist/__tests__/dna-detector.test.d.ts.map +1 -0
- package/dist/__tests__/dna-detector.test.js +97 -0
- package/dist/__tests__/dna-detector.test.js.map +1 -0
- package/dist/__tests__/dna-facts.test.d.ts +2 -0
- package/dist/__tests__/dna-facts.test.d.ts.map +1 -0
- package/dist/__tests__/dna-facts.test.js +44 -0
- package/dist/__tests__/dna-facts.test.js.map +1 -0
- package/dist/__tests__/graph-renderer.test.d.ts +2 -0
- package/dist/__tests__/graph-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/graph-renderer.test.js +85 -0
- package/dist/__tests__/graph-renderer.test.js.map +1 -0
- package/dist/__tests__/migration.test.d.ts +2 -0
- package/dist/__tests__/migration.test.d.ts.map +1 -0
- package/dist/__tests__/migration.test.js +77 -0
- package/dist/__tests__/migration.test.js.map +1 -0
- package/dist/__tests__/module-detector.test.d.ts +2 -0
- package/dist/__tests__/module-detector.test.d.ts.map +1 -0
- package/dist/__tests__/module-detector.test.js +83 -0
- package/dist/__tests__/module-detector.test.js.map +1 -0
- package/dist/__tests__/refine.test.js +49 -49
- package/dist/__tests__/reverse-facts.test.d.ts +2 -0
- package/dist/__tests__/reverse-facts.test.d.ts.map +1 -0
- package/dist/__tests__/reverse-facts.test.js +78 -0
- package/dist/__tests__/reverse-facts.test.js.map +1 -0
- package/dist/__tests__/review.test.js +38 -38
- package/dist/__tests__/validate.test.js +65 -65
- package/dist/bin/dare.js +32 -3
- package/dist/bin/dare.js.map +1 -1
- package/dist/commands/blueprint.js +122 -122
- package/dist/commands/dag.d.ts.map +1 -1
- package/dist/commands/dag.js +43 -79
- package/dist/commands/dag.js.map +1 -1
- package/dist/commands/dna.d.ts +3 -0
- package/dist/commands/dna.d.ts.map +1 -0
- package/dist/commands/dna.js +69 -0
- package/dist/commands/dna.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +101 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/new.d.ts +16 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +103 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/reverse.d.ts +3 -0
- package/dist/commands/reverse.d.ts.map +1 -0
- package/dist/commands/reverse.js +201 -0
- package/dist/commands/reverse.js.map +1 -0
- package/dist/commands/welcome.d.ts +14 -0
- package/dist/commands/welcome.d.ts.map +1 -0
- package/dist/commands/welcome.js +29 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/skills/commands/add.d.ts +23 -0
- package/dist/skills/commands/add.d.ts.map +1 -0
- package/dist/skills/commands/add.js +206 -0
- package/dist/skills/commands/add.js.map +1 -0
- package/dist/skills/commands/info.d.ts +14 -0
- package/dist/skills/commands/info.d.ts.map +1 -0
- package/dist/skills/commands/info.js +99 -0
- package/dist/skills/commands/info.js.map +1 -0
- package/dist/skills/commands/list.d.ts +19 -0
- package/dist/skills/commands/list.d.ts.map +1 -0
- package/dist/skills/commands/list.js +163 -0
- package/dist/skills/commands/list.js.map +1 -0
- package/dist/skills/commands/publish.d.ts +56 -0
- package/dist/skills/commands/publish.d.ts.map +1 -0
- package/dist/skills/commands/publish.js +272 -0
- package/dist/skills/commands/publish.js.map +1 -0
- package/dist/skills/commands/remove.d.ts +19 -0
- package/dist/skills/commands/remove.d.ts.map +1 -0
- package/dist/skills/commands/remove.js +96 -0
- package/dist/skills/commands/remove.js.map +1 -0
- package/dist/skills/commands/update.d.ts +31 -0
- package/dist/skills/commands/update.d.ts.map +1 -0
- package/dist/skills/commands/update.js +132 -0
- package/dist/skills/commands/update.js.map +1 -0
- package/dist/skills/index.d.ts +22 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +33 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/manifest.d.ts +54 -0
- package/dist/skills/manifest.d.ts.map +1 -0
- package/dist/skills/manifest.js +162 -0
- package/dist/skills/manifest.js.map +1 -0
- package/dist/skills/registry-local.d.ts +67 -0
- package/dist/skills/registry-local.d.ts.map +1 -0
- package/dist/skills/registry-local.js +130 -0
- package/dist/skills/registry-local.js.map +1 -0
- package/dist/skills/registry-mock.json +109 -0
- package/dist/skills/registry-remote.d.ts +110 -0
- package/dist/skills/registry-remote.d.ts.map +1 -0
- package/dist/skills/registry-remote.js +246 -0
- package/dist/skills/registry-remote.js.map +1 -0
- package/dist/skills/registry.d.ts +49 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +94 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/tests/manifest.spec.d.ts +8 -0
- package/dist/skills/tests/manifest.spec.d.ts.map +1 -0
- package/dist/skills/tests/manifest.spec.js +176 -0
- package/dist/skills/tests/manifest.spec.js.map +1 -0
- package/dist/skills/tests/publish.spec.d.ts +12 -0
- package/dist/skills/tests/publish.spec.d.ts.map +1 -0
- package/dist/skills/tests/publish.spec.js +276 -0
- package/dist/skills/tests/publish.spec.js.map +1 -0
- package/dist/skills/tests/registry-local.spec.d.ts +8 -0
- package/dist/skills/tests/registry-local.spec.d.ts.map +1 -0
- package/dist/skills/tests/registry-local.spec.js +231 -0
- package/dist/skills/tests/registry-local.spec.js.map +1 -0
- package/dist/skills/tests/registry.spec.d.ts +7 -0
- package/dist/skills/tests/registry.spec.d.ts.map +1 -0
- package/dist/skills/tests/registry.spec.js +58 -0
- package/dist/skills/tests/registry.spec.js.map +1 -0
- package/dist/skills/tests/remote-registry.spec.d.ts +9 -0
- package/dist/skills/tests/remote-registry.spec.d.ts.map +1 -0
- package/dist/skills/tests/remote-registry.spec.js +357 -0
- package/dist/skills/tests/remote-registry.spec.js.map +1 -0
- package/dist/skills/tests/update.spec.d.ts +9 -0
- package/dist/skills/tests/update.spec.d.ts.map +1 -0
- package/dist/skills/tests/update.spec.js +166 -0
- package/dist/skills/tests/update.spec.js.map +1 -0
- package/dist/utils/banner.d.ts +28 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +77 -0
- package/dist/utils/banner.js.map +1 -0
- package/dist/utils/banner.spec.d.ts +5 -0
- package/dist/utils/banner.spec.d.ts.map +1 -0
- package/dist/utils/banner.spec.js +253 -0
- package/dist/utils/banner.spec.js.map +1 -0
- package/dist/utils/confidence.d.ts +41 -0
- package/dist/utils/confidence.d.ts.map +1 -0
- package/dist/utils/confidence.js +101 -0
- package/dist/utils/confidence.js.map +1 -0
- package/dist/utils/datamodel.d.ts +41 -0
- package/dist/utils/datamodel.d.ts.map +1 -0
- package/dist/utils/datamodel.js +535 -0
- package/dist/utils/datamodel.js.map +1 -0
- package/dist/utils/dna-detector.d.ts +61 -0
- package/dist/utils/dna-detector.d.ts.map +1 -0
- package/dist/utils/dna-detector.js +354 -0
- package/dist/utils/dna-detector.js.map +1 -0
- package/dist/utils/dna-facts.d.ts +13 -0
- package/dist/utils/dna-facts.d.ts.map +1 -0
- package/dist/utils/dna-facts.js +109 -0
- package/dist/utils/dna-facts.js.map +1 -0
- package/dist/utils/excalidraw-renderer.d.ts +11 -71
- package/dist/utils/excalidraw-renderer.d.ts.map +1 -1
- package/dist/utils/excalidraw-renderer.js +29 -162
- package/dist/utils/excalidraw-renderer.js.map +1 -1
- package/dist/utils/graph-renderer.d.ts +115 -0
- package/dist/utils/graph-renderer.d.ts.map +1 -0
- package/dist/utils/graph-renderer.js +216 -0
- package/dist/utils/graph-renderer.js.map +1 -0
- package/dist/utils/migration.d.ts +64 -0
- package/dist/utils/migration.d.ts.map +1 -0
- package/dist/utils/migration.js +183 -0
- package/dist/utils/migration.js.map +1 -0
- package/dist/utils/module-detector.d.ts +46 -0
- package/dist/utils/module-detector.d.ts.map +1 -0
- package/dist/utils/module-detector.js +348 -0
- package/dist/utils/module-detector.js.map +1 -0
- package/dist/utils/project-generator.js +252 -252
- package/dist/utils/reverse-facts.d.ts +50 -0
- package/dist/utils/reverse-facts.d.ts.map +1 -0
- package/dist/utils/reverse-facts.js +291 -0
- package/dist/utils/reverse-facts.js.map +1 -0
- package/dist/utils/stack-bootstrap.js +371 -371
- package/package.json +8 -3
- package/templates/DARE-dag-example.yaml +280 -280
- package/templates/UPDATE-MANIFEST.json +48 -48
- package/templates/backend/node-nestjs/.env.example +9 -9
- package/templates/backend/node-nestjs/nest-cli.json +8 -8
- package/templates/backend/node-nestjs/package.json +50 -50
- package/templates/backend/node-nestjs/src/app.controller.ts +12 -12
- package/templates/backend/node-nestjs/src/app.module.ts +15 -15
- package/templates/backend/node-nestjs/src/app.service.ts +8 -8
- package/templates/backend/node-nestjs/src/main.ts +24 -24
- package/templates/backend/node-nestjs/tsconfig.json +21 -21
- package/templates/backend/php-laravel/.env.example +22 -22
- package/templates/backend/php-laravel/app/Http/Controllers/HealthController.php +15 -15
- package/templates/backend/php-laravel/composer.json +40 -40
- package/templates/backend/python-fastapi/.env.example +4 -4
- package/templates/backend/python-fastapi/app/api/router.py +8 -8
- package/templates/backend/python-fastapi/app/core/config.py +20 -20
- package/templates/backend/python-fastapi/main.py +35 -35
- package/templates/backend/python-fastapi/requirements.txt +13 -13
- package/templates/backend/rust-axum/.env.example +3 -3
- package/templates/backend/rust-axum/Cargo.toml +23 -23
- package/templates/backend/rust-axum/src/errors.rs +30 -30
- package/templates/backend/rust-axum/src/main.rs +32 -32
- package/templates/backend/rust-axum/src/routes.rs +6 -6
- package/templates/frontend/leptos-csr/.cargo/config.toml +2 -2
- package/templates/frontend/leptos-csr/Cargo.toml +16 -16
- package/templates/frontend/leptos-csr/Trunk.toml +10 -10
- package/templates/frontend/leptos-csr/index.html +11 -11
- package/templates/frontend/leptos-csr/src/lib.rs +20 -20
- package/templates/frontend/leptos-csr/style/main.scss +19 -19
- package/templates/frontend/leptos-fullstack/.cargo/config.toml +4 -4
- package/templates/frontend/leptos-fullstack/Cargo.toml +56 -56
- package/templates/frontend/leptos-fullstack/src/app.rs +49 -49
- package/templates/frontend/leptos-fullstack/src/lib.rs +9 -9
- package/templates/frontend/leptos-fullstack/src/main.rs +29 -29
- package/templates/frontend/leptos-fullstack/style/main.scss +19 -19
- package/templates/frontend/react/index.html +12 -12
- package/templates/frontend/react/package.json +35 -35
- package/templates/frontend/react/src/App.tsx +25 -25
- package/templates/frontend/react/src/main.tsx +9 -9
- package/templates/frontend/vue/package.json +32 -32
- package/templates/frontend/vue/src/App.vue +7 -7
- package/templates/frontend/vue/src/main.ts +10 -10
- package/templates/frontend/vue/src/router/index.ts +14 -14
- package/templates/frontend/vue/src/views/HomeView.vue +6 -6
- package/templates/hooks/pre-commit-dare-validate +24 -24
- package/templates/ide/antigravity/.agents/skills/dare-ax/SKILL.md +152 -0
- package/templates/ide/antigravity/.agents/skills/dare-dag-build/SKILL.md +154 -0
- package/templates/ide/antigravity/.agents/skills/dare-dag-run/SKILL.md +130 -0
- package/templates/ide/antigravity/.agents/skills/dare-dag-runner/SKILL.md +203 -203
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +63 -0
- package/templates/ide/antigravity/.agents/skills/dare-docker/SKILL.md +315 -0
- package/templates/ide/antigravity/.agents/skills/dare-frontend-design/SKILL.md +192 -0
- package/templates/ide/antigravity/.agents/skills/dare-laravel-api/SKILL.md +337 -0
- package/templates/ide/antigravity/.agents/skills/dare-layered-design/SKILL.md +166 -0
- package/templates/ide/antigravity/.agents/skills/dare-llm-integration/SKILL.md +217 -0
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +61 -0
- package/templates/ide/antigravity/.agents/skills/dare-quality-telemetry/SKILL.md +187 -0
- package/templates/ide/antigravity/.agents/skills/dare-realtime/SKILL.md +217 -0
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +114 -114
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +108 -0
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +111 -111
- package/templates/ide/antigravity/.agents/skills/dare-rust-leptos/SKILL.md +263 -0
- package/templates/ide/antigravity/.agents/skills/dare-rust-workspace/SKILL.md +275 -275
- package/templates/ide/antigravity/.agents/skills/dare-security/SKILL.md +274 -0
- package/templates/ide/antigravity/.agents/skills/dare-tasks/SKILL.md +265 -265
- package/templates/ide/antigravity/.agents/skills/dare-telemetry/SKILL.md +188 -0
- package/templates/ide/antigravity/.agents/skills/skill-fastapi-api/SKILL.md +343 -0
- package/templates/ide/antigravity/.agents/skills/skill-go-gin-api/SKILL.md +377 -0
- package/templates/ide/antigravity/.agents/skills/skill-mcp-server/SKILL.md +382 -0
- package/templates/ide/antigravity/.agents/skills/skill-nestjs-api/SKILL.md +326 -0
- package/templates/ide/antigravity/.agents/skills/skill-rails-api/SKILL.md +393 -0
- package/templates/ide/antigravity/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/antigravity/templates/DESIGN-template.md +129 -129
- package/templates/ide/antigravity/templates/TASK-SPEC-template.md +141 -141
- package/templates/ide/claude/.claude/commands/dare-ax.md +131 -0
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +134 -134
- package/templates/ide/claude/.claude/commands/dare-bugfix-design.md +119 -0
- package/templates/ide/claude/.claude/commands/dare-dag-build.md +151 -151
- package/templates/ide/claude/.claude/commands/dare-dag-run.md +109 -109
- package/templates/ide/claude/.claude/commands/dare-dag-runner.md +117 -0
- package/templates/ide/claude/.claude/commands/dare-dag-viz.md +197 -197
- package/templates/ide/claude/.claude/commands/dare-design.md +69 -69
- package/templates/ide/claude/.claude/commands/dare-dna.md +75 -0
- package/templates/ide/claude/.claude/commands/dare-docker.md +207 -0
- package/templates/ide/claude/.claude/commands/dare-execute.md +152 -152
- package/templates/ide/claude/.claude/commands/dare-feature-design.md +147 -0
- package/templates/ide/claude/.claude/commands/dare-frontend-design.md +149 -0
- package/templates/ide/claude/.claude/commands/dare-laravel-api.md +211 -0
- package/templates/ide/claude/.claude/commands/dare-layered-design.md +124 -0
- package/templates/ide/claude/.claude/commands/dare-llm-integration.md +148 -0
- package/templates/ide/claude/.claude/commands/dare-migrate.md +72 -0
- package/templates/ide/claude/.claude/commands/dare-quality-telemetry.md +166 -0
- package/templates/ide/claude/.claude/commands/dare-realtime.md +159 -0
- package/templates/ide/claude/.claude/commands/dare-refine.md +145 -145
- package/templates/ide/claude/.claude/commands/dare-reverse.md +139 -0
- package/templates/ide/claude/.claude/commands/dare-review.md +113 -113
- package/templates/ide/claude/.claude/commands/dare-rust-leptos.md +269 -269
- package/templates/ide/claude/.claude/commands/dare-rust-workspace.md +209 -209
- package/templates/ide/claude/.claude/commands/dare-security.md +232 -232
- package/templates/ide/claude/.claude/commands/dare-tasks.md +70 -70
- package/templates/ide/claude/.claude/commands/dare-telemetry.md +132 -0
- package/templates/ide/claude/.claude/commands/skill-fastapi-api.md +205 -0
- package/templates/ide/claude/.claude/commands/skill-go-gin-api.md +232 -0
- package/templates/ide/claude/.claude/commands/skill-mcp-server.md +228 -0
- package/templates/ide/claude/.claude/commands/skill-nestjs-api.md +210 -0
- package/templates/ide/claude/.claude/commands/skill-rails-api.md +236 -0
- package/templates/ide/claude/.claude/settings.example.json +35 -35
- package/templates/ide/claude/CLAUDE.md +146 -146
- package/templates/ide/claude/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/claude/templates/DESIGN-template.md +129 -129
- package/templates/ide/claude/templates/TASK-SPEC-template.md +141 -141
- package/templates/ide/cursor/.cursor/commands/dag-viz.md +139 -0
- package/templates/ide/cursor/.cursor/commands/generate-blueprint.md +86 -86
- package/templates/ide/cursor/.cursor/commands/generate-design.md +35 -35
- package/templates/ide/cursor/.cursor/commands/generate-tasks.md +184 -184
- package/templates/ide/cursor/.cursor/commands/refine-task.md +107 -107
- package/templates/ide/cursor/.cursor/commands/review-task.md +91 -91
- package/templates/ide/cursor/.cursor/commands/run-dag.md +110 -110
- package/templates/ide/cursor/.cursor/rules/skill-ax.mdc +263 -0
- package/templates/ide/cursor/.cursor/rules/skill-dag-build.mdc +173 -0
- package/templates/ide/cursor/.cursor/rules/skill-dag-run.mdc +134 -0
- package/templates/ide/cursor/.cursor/rules/skill-dag-runner.mdc +221 -221
- package/templates/ide/cursor/.cursor/rules/skill-dna.mdc +63 -0
- package/templates/ide/cursor/.cursor/rules/skill-fastapi-api.mdc +352 -0
- package/templates/ide/cursor/.cursor/rules/skill-frontend-design.mdc +244 -0
- package/templates/ide/cursor/.cursor/rules/skill-go-gin-api.mdc +371 -0
- package/templates/ide/cursor/.cursor/rules/skill-layered-design.mdc +266 -0
- package/templates/ide/cursor/.cursor/rules/skill-llm-integration.mdc +295 -0
- package/templates/ide/cursor/.cursor/rules/skill-mcp-server.mdc +367 -0
- package/templates/ide/cursor/.cursor/rules/skill-migrate.mdc +58 -0
- package/templates/ide/cursor/.cursor/rules/skill-nestjs-api.mdc +346 -0
- package/templates/ide/cursor/.cursor/rules/skill-quality-telemetry.mdc +248 -0
- package/templates/ide/cursor/.cursor/rules/skill-rails-api.mdc +400 -0
- package/templates/ide/cursor/.cursor/rules/skill-realtime.mdc +262 -0
- package/templates/ide/cursor/.cursor/rules/skill-reverse.mdc +107 -0
- package/templates/ide/cursor/.cursor/rules/skill-rust-leptos.mdc +281 -0
- package/templates/ide/cursor/.cursor/rules/skill-rust-workspace.mdc +312 -312
- package/templates/ide/cursor/.cursor/rules/skill-security.mdc +245 -245
- package/templates/ide/cursor/templates/BLUEPRINT-template.md +193 -193
- package/templates/ide/cursor/templates/DESIGN-template.md +129 -129
- package/templates/ide/cursor/templates/TASK-SPEC-template.md +141 -141
- package/templates/shared/docker-compose.yml +41 -41
- package/dist/__tests__/dag-runner/adapters.test.d.ts +0 -2
- package/dist/__tests__/dag-runner/adapters.test.d.ts.map +0 -1
- package/dist/__tests__/dag-runner/adapters.test.js +0 -134
- package/dist/__tests__/dag-runner/adapters.test.js.map +0 -1
- package/dist/dag-runner/adapters/antigravity.d.ts +0 -6
- package/dist/dag-runner/adapters/antigravity.d.ts.map +0 -1
- package/dist/dag-runner/adapters/antigravity.js +0 -54
- package/dist/dag-runner/adapters/antigravity.js.map +0 -1
- package/dist/dag-runner/adapters/claude.d.ts +0 -6
- package/dist/dag-runner/adapters/claude.d.ts.map +0 -1
- package/dist/dag-runner/adapters/claude.js +0 -48
- package/dist/dag-runner/adapters/claude.js.map +0 -1
- package/dist/dag-runner/adapters/cursor.d.ts +0 -6
- package/dist/dag-runner/adapters/cursor.d.ts.map +0 -1
- package/dist/dag-runner/adapters/cursor.js +0 -58
- package/dist/dag-runner/adapters/cursor.js.map +0 -1
- package/dist/dag-runner/adapters/index.d.ts +0 -46
- package/dist/dag-runner/adapters/index.d.ts.map +0 -1
- package/dist/dag-runner/adapters/index.js +0 -55
- package/dist/dag-runner/adapters/index.js.map +0 -1
- package/dist/dag-runner/utils/timeout.d.ts +0 -27
- package/dist/dag-runner/utils/timeout.d.ts.map +0 -1
- package/dist/dag-runner/utils/timeout.js +0 -55
- package/dist/dag-runner/utils/timeout.js.map +0 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Integração segura e eficiente com LLMs (Gemini, Claude, OpenAI, Ollama) em projetos DARE. Fornece LLMProvider abstraction, cache TTL, rate limit via token bucket, prompts versionados e validação de output via JSON Schema.
|
|
3
|
+
globs: prompts/**/*.md,**/llm*.ts,**/llm*.py,**/llm*.rs,**/llm*.go,**/providers/**
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: LLM Integration DARE
|
|
8
|
+
|
|
9
|
+
Você é um especialista em integração com LLMs. Esta skill garante que toda chamada a LLM em projeto DARE seja **abstraída, cacheada, rate-limited, validada e defendida contra prompt injection**.
|
|
10
|
+
|
|
11
|
+
## Arquitetura recomendada
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
┌──────────────────────────────────────────────────────┐
|
|
15
|
+
│ Handler / Service de negócio │
|
|
16
|
+
└──────────────────────────────────────────────────────┘
|
|
17
|
+
↓ injeta
|
|
18
|
+
┌──────────────────────────────────────────────────────┐
|
|
19
|
+
│ LLMProvider (interface) │
|
|
20
|
+
│ ├── GeminiProvider │
|
|
21
|
+
│ ├── ClaudeProvider │
|
|
22
|
+
│ ├── OpenAIProvider │
|
|
23
|
+
│ └── OllamaProvider (local) │
|
|
24
|
+
└──────────────────────────────────────────────────────┘
|
|
25
|
+
↓ wrap
|
|
26
|
+
┌──────────────────────────────────────────────────────┐
|
|
27
|
+
│ Cache (TTL) + RateLimit (token bucket) + Schema │
|
|
28
|
+
└──────────────────────────────────────────────────────┘
|
|
29
|
+
↓
|
|
30
|
+
┌──────────────────────────────────────────────────────┐
|
|
31
|
+
│ HTTP call externo │
|
|
32
|
+
└──────────────────────────────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Os 5 pilares
|
|
36
|
+
|
|
37
|
+
### 1. LLMProvider abstraction
|
|
38
|
+
|
|
39
|
+
Nunca chame SDK direto em Handler ou Service de negócio.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// ❌ Errado
|
|
43
|
+
class SummaryService {
|
|
44
|
+
async run(text: string) {
|
|
45
|
+
const client = new GoogleGenAI({ apiKey: 'xxx' });
|
|
46
|
+
return client.generateContent({ contents: text });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ✅ Certo
|
|
51
|
+
class SummaryService {
|
|
52
|
+
constructor(private llm: LLMProvider) {}
|
|
53
|
+
async run(text: string) {
|
|
54
|
+
return this.llm.complete({
|
|
55
|
+
promptId: 'summarize-v1',
|
|
56
|
+
input: { text },
|
|
57
|
+
schema: SummarySchema,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Cache em memória com TTL
|
|
64
|
+
|
|
65
|
+
Key = `hash(promptId + input + model)`. TTL configurável (default 1h).
|
|
66
|
+
|
|
67
|
+
**AP-02:** sem cache = custo explode em loops + latência ruim.
|
|
68
|
+
|
|
69
|
+
### 3. Rate limit via token bucket
|
|
70
|
+
|
|
71
|
+
| Provider | Limite típico |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Gemini Free | 15 RPM, 1M tokens/dia |
|
|
74
|
+
| Claude tier 1 | 50 RPM |
|
|
75
|
+
| OpenAI tier 1 | 500 RPM |
|
|
76
|
+
| Ollama local | sem limite, latência alta |
|
|
77
|
+
|
|
78
|
+
Token bucket por provider, configurado em `app.config`.
|
|
79
|
+
|
|
80
|
+
### 4. Prompts versionados
|
|
81
|
+
|
|
82
|
+
Prompts vivem em `prompts/<id>.v<n>.md`:
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
---
|
|
86
|
+
id: summarize
|
|
87
|
+
version: 1
|
|
88
|
+
model: gemini-2.0-flash
|
|
89
|
+
temperature: 0.2
|
|
90
|
+
max_tokens: 500
|
|
91
|
+
schema: SummarySchema
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
# System
|
|
95
|
+
You are a concise summarizer. Output JSON matching: {schema}
|
|
96
|
+
|
|
97
|
+
# User
|
|
98
|
+
Summarize the following text in 3 bullet points:
|
|
99
|
+
|
|
100
|
+
<text>{{ text }}</text>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Substituição com template engine **seguro** (Jinja2 sandboxed, Handlebars, string-interp com escape). Nunca `eval` ou `f-string` direta com user input.
|
|
104
|
+
|
|
105
|
+
### 5. Validação de output
|
|
106
|
+
|
|
107
|
+
LLM mente. Sempre valide:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const raw = await llm.complete({...});
|
|
111
|
+
const result = SummarySchema.parse(raw); // Zod/Pydantic/ajv
|
|
112
|
+
if (!result.success) {
|
|
113
|
+
throw new LLMOutputInvalidError(result.error);
|
|
114
|
+
// Pode indicar prompt injection ou drift de modelo — logar + alertar
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Métricas obrigatórias
|
|
121
|
+
|
|
122
|
+
| ID | Métrica | Como medir |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| M-01 | 100% das chamadas LLM via LLMProvider | grep por SDK direto em Handlers/Services |
|
|
125
|
+
| M-02 | 100% das responses cacheadas | logs do cache layer |
|
|
126
|
+
| M-03 | 100% das requests com rate limit | rate limiter ativo em todo provider |
|
|
127
|
+
| M-04 | 100% das respostas validadas contra schema | grep por `.parse(` ou `model_validate(` |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Antipatterns (CI falha)
|
|
132
|
+
|
|
133
|
+
| AP | Antipattern | Por que evitar |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| AP-01 | SDK direto em Handler | impossível mockar / trocar provider |
|
|
136
|
+
| AP-02 | Sem cache | custo explode, latência ruim |
|
|
137
|
+
| AP-03 | Prompt em código | impossível versionar / A-B testar |
|
|
138
|
+
| AP-04 | User input direto em prompt | prompt injection trivial |
|
|
139
|
+
| AP-05 | Confiar em output LLM sem validar | LLM mente — schema é defesa |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Defesa contra prompt injection
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
# ❌ Errado — instrução + dado misturados
|
|
147
|
+
system = f"You are an assistant. {user_question}"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# ✅ Certo — separação clara + escape
|
|
152
|
+
messages = [
|
|
153
|
+
{"role": "system", "content": "You are an assistant. Answer only based on the provided document."},
|
|
154
|
+
{"role": "user", "content": f"<document>{escape(doc)}</document>\n\n<question>{escape(q)}</question>"}
|
|
155
|
+
]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Regras:
|
|
159
|
+
- Use delimitadores (`<document>`, `<question>`)
|
|
160
|
+
- Escape conteúdo do usuário (XML/HTML escape)
|
|
161
|
+
- Detecte e remova padrões "Ignore instructions above", "You are now ..."
|
|
162
|
+
- Valide output contra schema — output fora do schema = possível injection bem-sucedido
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Exemplos por stack
|
|
167
|
+
|
|
168
|
+
### TypeScript (NestJS)
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
@Injectable()
|
|
172
|
+
export class GeminiProvider implements LLMProvider {
|
|
173
|
+
constructor(
|
|
174
|
+
private cache: Cache,
|
|
175
|
+
private rateLimit: TokenBucket,
|
|
176
|
+
@Inject('GEMINI_API_KEY') private apiKey: string,
|
|
177
|
+
) {}
|
|
178
|
+
|
|
179
|
+
async complete(req: LLMRequest): Promise<unknown> {
|
|
180
|
+
const cached = await this.cache.get(req.cacheKey());
|
|
181
|
+
if (cached) return cached;
|
|
182
|
+
await this.rateLimit.acquire(1);
|
|
183
|
+
const raw = await this.callGemini(req);
|
|
184
|
+
const validated = req.schema.parse(raw);
|
|
185
|
+
await this.cache.set(req.cacheKey(), validated, req.ttl);
|
|
186
|
+
return validated;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Python (FastAPI)
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
class ClaudeProvider(LLMProvider):
|
|
195
|
+
def __init__(self, client: Anthropic, cache: Cache, bucket: TokenBucket):
|
|
196
|
+
self.client = client
|
|
197
|
+
self.cache = cache
|
|
198
|
+
self.bucket = bucket
|
|
199
|
+
|
|
200
|
+
async def complete(self, req: LLMRequest):
|
|
201
|
+
cached = await self.cache.get(req.cache_key())
|
|
202
|
+
if cached: return cached
|
|
203
|
+
await self.bucket.acquire(1)
|
|
204
|
+
raw = await self.client.messages.create(...)
|
|
205
|
+
validated = req.schema.model_validate(raw.content[0].text)
|
|
206
|
+
await self.cache.set(req.cache_key(), validated.model_dump(), req.ttl)
|
|
207
|
+
return validated
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Rust/Axum
|
|
211
|
+
|
|
212
|
+
```rust
|
|
213
|
+
pub struct GeminiProvider {
|
|
214
|
+
client: reqwest::Client,
|
|
215
|
+
cache: Arc<dyn Cache>,
|
|
216
|
+
bucket: Arc<TokenBucket>,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#[async_trait]
|
|
220
|
+
impl LLMProvider for GeminiProvider {
|
|
221
|
+
async fn complete<T: DeserializeOwned + JsonSchema>(&self, req: LLMRequest<T>) -> Result<T> {
|
|
222
|
+
if let Some(cached) = self.cache.get(&req.cache_key()).await? {
|
|
223
|
+
return Ok(cached);
|
|
224
|
+
}
|
|
225
|
+
self.bucket.acquire(1).await?;
|
|
226
|
+
let raw = self.call_gemini(&req).await?;
|
|
227
|
+
let validated: T = serde_json::from_value(raw)?;
|
|
228
|
+
self.cache.set(&req.cache_key(), &validated, req.ttl).await?;
|
|
229
|
+
Ok(validated)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Observabilidade obrigatória
|
|
237
|
+
|
|
238
|
+
Logue estruturado por chamada:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"promptId": "summarize",
|
|
243
|
+
"version": 1,
|
|
244
|
+
"model": "gemini-2.0-flash",
|
|
245
|
+
"cacheHit": false,
|
|
246
|
+
"tokensIn": 1342,
|
|
247
|
+
"tokensOut": 287,
|
|
248
|
+
"estimatedCostUsd": 0.0021,
|
|
249
|
+
"latencyMs": 850,
|
|
250
|
+
"schemaValid": true
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Não logue: prompt completo (privacidade), output completo (PII), API keys.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Aplicação por fase DARE
|
|
259
|
+
|
|
260
|
+
### Design
|
|
261
|
+
- Listar quais LLMs serão usados (provider, modelo)
|
|
262
|
+
- Estimar volume de calls/dia → ajustar rate limit
|
|
263
|
+
- Definir prompts iniciais com schema
|
|
264
|
+
|
|
265
|
+
### Blueprint
|
|
266
|
+
- Diagrama da camada LLM (provider, cache, rate limit)
|
|
267
|
+
- Estratégia de fallback (primary → secondary)
|
|
268
|
+
- Estratégia de versionamento de prompts
|
|
269
|
+
|
|
270
|
+
### Tasks
|
|
271
|
+
- Task dedicada: "Criar LLMProvider + cache + rate limit"
|
|
272
|
+
- Task por prompt: "Extrair prompt X para `prompts/x.v1.md`"
|
|
273
|
+
- Task de validação: "Adicionar schema Zod/Pydantic para cada prompt"
|
|
274
|
+
|
|
275
|
+
### Execute
|
|
276
|
+
- Ralph Loop: grep falha se SDK direto em Service/Handler
|
|
277
|
+
- Métricas M-01 a M-04 reportadas em cada PR
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Checklist final
|
|
282
|
+
|
|
283
|
+
- [ ] Interface `LLMProvider` definida
|
|
284
|
+
- [ ] Pelo menos 1 provider implementado
|
|
285
|
+
- [ ] Cache em memória com TTL configurado
|
|
286
|
+
- [ ] Token bucket configurado por provider
|
|
287
|
+
- [ ] Todos os prompts em `prompts/<id>.v<n>.md`
|
|
288
|
+
- [ ] Schema para cada prompt
|
|
289
|
+
- [ ] Validação de output em todo chamador
|
|
290
|
+
- [ ] Logs estruturados com tokens + custo
|
|
291
|
+
- [ ] Defesa contra prompt injection em prompts com user input
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
Skill licenciada MIT — parte do DARE Method v3.
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Padrões DARE para servidores MCP (Model Context Protocol da Anthropic) em TypeScript ou Python. Tools, resources, prompts; transports stdio/SSE/HTTP; validação Zod/Pydantic; autorização por tool; observabilidade; testes; publicação.
|
|
3
|
+
globs: **/*.ts,**/*.py,package.json,pyproject.toml,claude_desktop_config.json
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: MCP Server DARE
|
|
8
|
+
|
|
9
|
+
Você é um especialista em servidores MCP (Model Context Protocol). Esta skill garante MCP servers **bem estruturados, seguros, testáveis e publicáveis**, expondo tools/resources/prompts via stdio, SSE ou HTTP.
|
|
10
|
+
|
|
11
|
+
## O que é MCP em 30 segundos
|
|
12
|
+
|
|
13
|
+
Protocolo aberto da Anthropic para conectar agentes (Claude Code, Claude Desktop, Cursor, etc.) a fontes de dados e ações externas. Server expõe:
|
|
14
|
+
|
|
15
|
+
- **Tools** — funções chamáveis pelo agente
|
|
16
|
+
- **Resources** — dados leitáveis (URI + mimeType)
|
|
17
|
+
- **Prompts** — templates parametrizados
|
|
18
|
+
|
|
19
|
+
Transports:
|
|
20
|
+
|
|
21
|
+
| Transport | Quando usar |
|
|
22
|
+
|---|---|
|
|
23
|
+
| **stdio** | Server local, agente spawnea processo |
|
|
24
|
+
| **SSE** | Server remoto, streaming bidirectional |
|
|
25
|
+
| **HTTP** | Server remoto stateless |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Stack canônica
|
|
30
|
+
|
|
31
|
+
### TypeScript
|
|
32
|
+
|
|
33
|
+
- Node 20+ + TS 5.5+
|
|
34
|
+
- `@modelcontextprotocol/sdk` SDK oficial
|
|
35
|
+
- Zod para schemas
|
|
36
|
+
- vitest
|
|
37
|
+
|
|
38
|
+
### Python
|
|
39
|
+
|
|
40
|
+
- Python 3.11+
|
|
41
|
+
- `mcp` (SDK oficial, `pip install mcp`)
|
|
42
|
+
- Pydantic v2
|
|
43
|
+
- pytest + pytest-asyncio
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Estrutura
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
mcp-meu-server/
|
|
51
|
+
├── package.json (ou pyproject.toml)
|
|
52
|
+
├── src/
|
|
53
|
+
│ ├── index.ts ← stdio entrypoint
|
|
54
|
+
│ ├── server.ts ← criação do MCP Server
|
|
55
|
+
│ ├── tools/
|
|
56
|
+
│ │ ├── create_issue.ts
|
|
57
|
+
│ │ └── search_issues.ts
|
|
58
|
+
│ ├── resources/
|
|
59
|
+
│ ├── prompts/
|
|
60
|
+
│ ├── schemas/
|
|
61
|
+
│ └── lib/ ← clientes externos
|
|
62
|
+
└── tests/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Exemplo TypeScript (stdio)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// src/index.ts
|
|
71
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
72
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
73
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
74
|
+
import { z } from 'zod';
|
|
75
|
+
|
|
76
|
+
const CreateIssueInput = z.object({
|
|
77
|
+
title: z.string().min(1).max(255),
|
|
78
|
+
description: z.string().optional(),
|
|
79
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
80
|
+
team_id: z.string().uuid(),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const server = new Server(
|
|
84
|
+
{ name: 'meu-server', version: '1.0.0' },
|
|
85
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
89
|
+
tools: [{
|
|
90
|
+
name: 'create_issue',
|
|
91
|
+
description: 'Create a new issue in Linear with the given title, description, and priority',
|
|
92
|
+
inputSchema: CreateIssueInput.shape,
|
|
93
|
+
}],
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
97
|
+
if (req.params.name !== 'create_issue') throw new Error('unknown tool');
|
|
98
|
+
const input = CreateIssueInput.parse(req.params.arguments);
|
|
99
|
+
const result = await linearClient.createIssue(input);
|
|
100
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const transport = new StdioServerTransport();
|
|
104
|
+
await server.connect(transport);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Exemplo Python (stdio)
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
import asyncio
|
|
113
|
+
from mcp.server import Server
|
|
114
|
+
from mcp.server.stdio import stdio_server
|
|
115
|
+
from mcp.types import Tool, TextContent
|
|
116
|
+
from pydantic import BaseModel, Field
|
|
117
|
+
|
|
118
|
+
app = Server("meu-server")
|
|
119
|
+
|
|
120
|
+
class CreateIssueInput(BaseModel):
|
|
121
|
+
title: str = Field(min_length=1, max_length=255)
|
|
122
|
+
description: str | None = None
|
|
123
|
+
priority: str = Field(default="medium", pattern="^(low|medium|high)$")
|
|
124
|
+
team_id: str
|
|
125
|
+
|
|
126
|
+
@app.list_tools()
|
|
127
|
+
async def list_tools() -> list[Tool]:
|
|
128
|
+
return [
|
|
129
|
+
Tool(
|
|
130
|
+
name="create_issue",
|
|
131
|
+
description="Create a new issue in Linear",
|
|
132
|
+
inputSchema=CreateIssueInput.model_json_schema(),
|
|
133
|
+
)
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
@app.call_tool()
|
|
137
|
+
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|
138
|
+
if name != "create_issue":
|
|
139
|
+
raise ValueError(f"unknown tool: {name}")
|
|
140
|
+
input_data = CreateIssueInput.model_validate(arguments)
|
|
141
|
+
result = await linear_client.create_issue(input_data)
|
|
142
|
+
return [TextContent(type="text", text=result.model_dump_json())]
|
|
143
|
+
|
|
144
|
+
async def main():
|
|
145
|
+
async with stdio_server() as (read, write):
|
|
146
|
+
await app.run(read, write, app.create_initialization_options())
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
asyncio.run(main())
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Resources
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
158
|
+
resources: [{
|
|
159
|
+
uri: 'linear://schema',
|
|
160
|
+
name: 'Linear Issue Schema',
|
|
161
|
+
mimeType: 'application/json',
|
|
162
|
+
}],
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
166
|
+
if (req.params.uri === 'linear://schema') {
|
|
167
|
+
return {
|
|
168
|
+
contents: [{
|
|
169
|
+
uri: req.params.uri,
|
|
170
|
+
mimeType: 'application/json',
|
|
171
|
+
text: JSON.stringify(schema),
|
|
172
|
+
}],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
throw new Error('unknown resource');
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Prompts
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
185
|
+
prompts: [{
|
|
186
|
+
name: 'triage_issue',
|
|
187
|
+
description: 'Triage an issue and assign priority',
|
|
188
|
+
arguments: [
|
|
189
|
+
{ name: 'description', description: 'Issue description', required: true },
|
|
190
|
+
],
|
|
191
|
+
}],
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
server.setRequestHandler(GetPromptRequestSchema, async (req) => ({
|
|
195
|
+
messages: [{
|
|
196
|
+
role: 'user',
|
|
197
|
+
content: {
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: `Triage this issue and suggest a priority:\n\n${req.params.arguments?.description}`,
|
|
200
|
+
},
|
|
201
|
+
}],
|
|
202
|
+
}));
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Autorização por tool
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Tool destrutiva exige confirm explícito
|
|
211
|
+
if (input.confirm !== 'YES_I_AM_SURE') {
|
|
212
|
+
throw new Error('this tool requires confirmation');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Tool admin
|
|
216
|
+
if (!callerHasRole('admin')) {
|
|
217
|
+
throw new Error('admin only');
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Observabilidade
|
|
224
|
+
|
|
225
|
+
**Crítico em stdio:** o protocolo MCP usa **stdout**. Logs SEMPRE em stderr.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
console.error(JSON.stringify({
|
|
229
|
+
ts: new Date().toISOString(),
|
|
230
|
+
tool: req.params.name,
|
|
231
|
+
duration_ms: Date.now() - start,
|
|
232
|
+
success: true,
|
|
233
|
+
// sem args (PII), sem result completo (pode ter PII)
|
|
234
|
+
}));
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Testes
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// Unit
|
|
243
|
+
import { test, expect } from 'vitest';
|
|
244
|
+
test('valida input correto', () => {
|
|
245
|
+
const valid = CreateIssueInput.parse({
|
|
246
|
+
title: 'Test',
|
|
247
|
+
team_id: '550e8400-e29b-41d4-a716-446655440000',
|
|
248
|
+
});
|
|
249
|
+
expect(valid.priority).toBe('medium');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('rejeita team_id inválido', () => {
|
|
253
|
+
expect(() => CreateIssueInput.parse({
|
|
254
|
+
title: 'Test',
|
|
255
|
+
team_id: 'not-uuid',
|
|
256
|
+
})).toThrow();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// E2E com client MCP
|
|
260
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
261
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
262
|
+
|
|
263
|
+
const transport = new StdioClientTransport({ command: 'node', args: ['dist/index.js'] });
|
|
264
|
+
const client = new Client({ name: 'test', version: '0.0.0' }, { capabilities: {} });
|
|
265
|
+
await client.connect(transport);
|
|
266
|
+
const tools = await client.listTools();
|
|
267
|
+
expect(tools.tools.map(t => t.name)).toContain('create_issue');
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Antipatterns
|
|
273
|
+
|
|
274
|
+
| AP | Antipattern | Por quê |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| AP-01 | Logs no stdout (stdio) | Quebra o protocolo MCP |
|
|
277
|
+
| AP-02 | Tool sem schema de input | Cliente passa lixo |
|
|
278
|
+
| AP-03 | Sem validação no server | Confia no client (errado) |
|
|
279
|
+
| AP-04 | Tool destrutiva sem confirm | Agente apaga sem querer |
|
|
280
|
+
| AP-05 | Secrets em código | Use env vars |
|
|
281
|
+
| AP-06 | Tool name vago (`do_thing`) | Agente não sabe quando usar |
|
|
282
|
+
| AP-07 | Description curto/vago | Agente erra na escolha |
|
|
283
|
+
| AP-08 | Resource sem mimeType | Cliente não parseia |
|
|
284
|
+
| AP-09 | Tool sem error handling | Erro vira crash em vez de mensagem |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Publicação
|
|
289
|
+
|
|
290
|
+
### Pré-requisitos
|
|
291
|
+
|
|
292
|
+
- `README.md` com:
|
|
293
|
+
- Instalação e configuração
|
|
294
|
+
- Lista de tools/resources/prompts
|
|
295
|
+
- Variáveis de ambiente
|
|
296
|
+
- Exemplo `claude_desktop_config.json`
|
|
297
|
+
- `LICENSE` (MIT recomendado)
|
|
298
|
+
- `package.json`/`pyproject.toml` versionado
|
|
299
|
+
|
|
300
|
+
### Distribuição
|
|
301
|
+
|
|
302
|
+
| Stack | Comando |
|
|
303
|
+
|---|---|
|
|
304
|
+
| TypeScript | `npm publish` |
|
|
305
|
+
| Python | `python -m build && twine upload dist/*` |
|
|
306
|
+
| Docker (SSE/HTTP) | multi-stage build + push para registry |
|
|
307
|
+
|
|
308
|
+
### Config no Claude Desktop
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"mcpServers": {
|
|
313
|
+
"meu-server": {
|
|
314
|
+
"command": "node",
|
|
315
|
+
"args": ["/abs/path/to/dist/index.js"],
|
|
316
|
+
"env": {
|
|
317
|
+
"LINEAR_API_KEY": "lin_api_..."
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Aplicação por fase DARE
|
|
327
|
+
|
|
328
|
+
### Design
|
|
329
|
+
- Definir capabilities (tools, resources, prompts) com nomes e descrições claras
|
|
330
|
+
- Decidir transport (stdio/SSE/HTTP)
|
|
331
|
+
- Listar variáveis de ambiente necessárias
|
|
332
|
+
|
|
333
|
+
### Blueprint
|
|
334
|
+
- Estrutura de pastas
|
|
335
|
+
- Schemas Zod/Pydantic por tool
|
|
336
|
+
- Estratégia de autorização
|
|
337
|
+
|
|
338
|
+
### Tasks
|
|
339
|
+
- Por tool: 1 task (schema + handler + teste)
|
|
340
|
+
- Task de configuração transport + entrypoint
|
|
341
|
+
- Task de publicação (README + LICENSE + CI release)
|
|
342
|
+
|
|
343
|
+
### Execute
|
|
344
|
+
- Ralph Loop:
|
|
345
|
+
```bash
|
|
346
|
+
npm run lint && npm test && npm run build
|
|
347
|
+
# ou
|
|
348
|
+
ruff check . && mypy . && pytest
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Checklist final
|
|
354
|
+
|
|
355
|
+
- [ ] Transport escolhido (stdio/SSE/HTTP) com justificativa
|
|
356
|
+
- [ ] Cada tool tem name descritivo, description completa, input schema
|
|
357
|
+
- [ ] Validação Zod/Pydantic no início de cada handler
|
|
358
|
+
- [ ] Tools destrutivas exigem `confirm`
|
|
359
|
+
- [ ] Logs em stderr (stdio) com JSON estruturado
|
|
360
|
+
- [ ] Secrets via env vars, nunca em código
|
|
361
|
+
- [ ] Testes unit + E2E com client MCP
|
|
362
|
+
- [ ] README + LICENSE MIT
|
|
363
|
+
- [ ] Publicação configurada (npm/PyPI/Docker)
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
Skill licenciada MIT — parte do DARE Method v3.
|