@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,382 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-mcp-server
|
|
3
|
+
description: Padrões DARE para servidores MCP (Model Context Protocol) em TypeScript ou Python. Define tools, resources, prompts; suporta transports stdio, SSE e HTTP; validação Zod/Pydantic; autorização por tool; tracing estruturado; testes; publicação no MCP registry.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DARE MCP Server Skill
|
|
7
|
+
|
|
8
|
+
Você é um especialista em servidores MCP (Model Context Protocol da Anthropic). Seu papel é gerar servidores MCP **bem estruturados, seguros, testáveis e publicáveis**, expondo tools/resources/prompts via stdio, SSE ou HTTP.
|
|
9
|
+
|
|
10
|
+
## Quando usar
|
|
11
|
+
|
|
12
|
+
- Você precisa expor capacidades para Claude Code, Cursor ou outro cliente MCP
|
|
13
|
+
- Você quer integrar uma API externa (Linear, GitHub, banco interno) como tool MCP
|
|
14
|
+
- Você está auditando um servidor MCP existente
|
|
15
|
+
|
|
16
|
+
## O que é MCP em 30 segundos
|
|
17
|
+
|
|
18
|
+
MCP (Model Context Protocol) é um protocolo aberto da Anthropic para conectar agentes a fontes de dados e ações externas. Um servidor MCP expõe:
|
|
19
|
+
|
|
20
|
+
- **Tools** — funções chamáveis pelo agente (ex: `create_issue`, `query_db`)
|
|
21
|
+
- **Resources** — dados leitáveis (ex: documentos, schemas)
|
|
22
|
+
- **Prompts** — templates parametrizados
|
|
23
|
+
|
|
24
|
+
Transports suportados:
|
|
25
|
+
- **stdio** — agente spawnea processo local, comunica via stdin/stdout (mais simples)
|
|
26
|
+
- **SSE** — server remoto, agente conecta via Server-Sent Events
|
|
27
|
+
- **HTTP** — server remoto stateless
|
|
28
|
+
|
|
29
|
+
## Stack canônica (TypeScript)
|
|
30
|
+
|
|
31
|
+
- **Node 20+** com TypeScript 5.5+
|
|
32
|
+
- **@modelcontextprotocol/sdk** SDK oficial
|
|
33
|
+
- **Zod** para validação de input/output de tools
|
|
34
|
+
- **vitest** para testes
|
|
35
|
+
- **eslint + prettier**
|
|
36
|
+
|
|
37
|
+
## Stack canônica (Python)
|
|
38
|
+
|
|
39
|
+
- **Python 3.11+**
|
|
40
|
+
- **mcp** SDK oficial (`pip install mcp`)
|
|
41
|
+
- **Pydantic v2** para schemas
|
|
42
|
+
- **pytest + pytest-asyncio**
|
|
43
|
+
- **ruff + mypy**
|
|
44
|
+
|
|
45
|
+
## Estrutura recomendada (TypeScript)
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
mcp-meu-server/
|
|
49
|
+
├── package.json
|
|
50
|
+
├── tsconfig.json
|
|
51
|
+
├── src/
|
|
52
|
+
│ ├── index.ts ← entrypoint (stdio)
|
|
53
|
+
│ ├── server.ts ← criação do MCP Server
|
|
54
|
+
│ ├── tools/
|
|
55
|
+
│ │ ├── create_issue.ts
|
|
56
|
+
│ │ └── search_issues.ts
|
|
57
|
+
│ ├── resources/
|
|
58
|
+
│ │ └── schema.ts
|
|
59
|
+
│ ├── prompts/
|
|
60
|
+
│ │ └── issue_template.ts
|
|
61
|
+
│ ├── schemas/ ← Zod schemas
|
|
62
|
+
│ └── lib/
|
|
63
|
+
│ └── linear-client.ts ← cliente da API externa
|
|
64
|
+
└── tests/
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Exemplo: server stdio TypeScript
|
|
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 { registerTools } from './tools/index.js';
|
|
74
|
+
import { registerResources } from './resources/index.js';
|
|
75
|
+
|
|
76
|
+
const server = new Server(
|
|
77
|
+
{ name: 'meu-mcp-server', version: '1.0.0' },
|
|
78
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
registerTools(server);
|
|
82
|
+
registerResources(server);
|
|
83
|
+
|
|
84
|
+
const transport = new StdioServerTransport();
|
|
85
|
+
await server.connect(transport);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Definindo uma tool
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// src/tools/create_issue.ts
|
|
92
|
+
import { z } from 'zod';
|
|
93
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
94
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
95
|
+
|
|
96
|
+
const CreateIssueInput = z.object({
|
|
97
|
+
title: z.string().min(1).max(255),
|
|
98
|
+
description: z.string().optional(),
|
|
99
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
100
|
+
team_id: z.string().uuid(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const CreateIssueOutput = z.object({
|
|
104
|
+
id: z.string(),
|
|
105
|
+
url: z.string().url(),
|
|
106
|
+
number: z.number(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export function register(server: Server) {
|
|
110
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
111
|
+
tools: [{
|
|
112
|
+
name: 'create_issue',
|
|
113
|
+
description: 'Create a new issue in Linear',
|
|
114
|
+
inputSchema: CreateIssueInput.shape, // ou gerar via zod-to-json-schema
|
|
115
|
+
}],
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
119
|
+
if (req.params.name !== 'create_issue') {
|
|
120
|
+
throw new Error('unknown tool');
|
|
121
|
+
}
|
|
122
|
+
const input = CreateIssueInput.parse(req.params.arguments);
|
|
123
|
+
const result = await linearClient.createIssue(input);
|
|
124
|
+
const output = CreateIssueOutput.parse({
|
|
125
|
+
id: result.id,
|
|
126
|
+
url: result.url,
|
|
127
|
+
number: result.number,
|
|
128
|
+
});
|
|
129
|
+
return { content: [{ type: 'text', text: JSON.stringify(output) }] };
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Exemplo: server stdio Python
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# server.py
|
|
138
|
+
import asyncio
|
|
139
|
+
from mcp.server import Server
|
|
140
|
+
from mcp.server.stdio import stdio_server
|
|
141
|
+
from mcp.types import Tool, TextContent
|
|
142
|
+
from pydantic import BaseModel, Field
|
|
143
|
+
|
|
144
|
+
app = Server("meu-mcp-server")
|
|
145
|
+
|
|
146
|
+
class CreateIssueInput(BaseModel):
|
|
147
|
+
title: str = Field(min_length=1, max_length=255)
|
|
148
|
+
description: str | None = None
|
|
149
|
+
priority: str = Field(default="medium", pattern="^(low|medium|high)$")
|
|
150
|
+
team_id: str
|
|
151
|
+
|
|
152
|
+
@app.list_tools()
|
|
153
|
+
async def list_tools() -> list[Tool]:
|
|
154
|
+
return [
|
|
155
|
+
Tool(
|
|
156
|
+
name="create_issue",
|
|
157
|
+
description="Create a new issue in Linear",
|
|
158
|
+
inputSchema=CreateIssueInput.model_json_schema(),
|
|
159
|
+
)
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
@app.call_tool()
|
|
163
|
+
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|
164
|
+
if name != "create_issue":
|
|
165
|
+
raise ValueError(f"unknown tool: {name}")
|
|
166
|
+
input_data = CreateIssueInput.model_validate(arguments)
|
|
167
|
+
result = await linear_client.create_issue(input_data)
|
|
168
|
+
return [TextContent(type="text", text=result.model_dump_json())]
|
|
169
|
+
|
|
170
|
+
async def main():
|
|
171
|
+
async with stdio_server() as (read, write):
|
|
172
|
+
await app.run(read, write, app.create_initialization_options())
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
asyncio.run(main())
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Resources
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
182
|
+
|
|
183
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
184
|
+
resources: [{
|
|
185
|
+
uri: 'linear://schema',
|
|
186
|
+
name: 'Linear Issue Schema',
|
|
187
|
+
mimeType: 'application/json',
|
|
188
|
+
}],
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
192
|
+
if (req.params.uri === 'linear://schema') {
|
|
193
|
+
return { contents: [{ uri: req.params.uri, mimeType: 'application/json', text: JSON.stringify(schema) }] };
|
|
194
|
+
}
|
|
195
|
+
throw new Error('unknown resource');
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Prompts
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
203
|
+
|
|
204
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
205
|
+
prompts: [{
|
|
206
|
+
name: 'triage_issue',
|
|
207
|
+
description: 'Triage an issue and assign priority',
|
|
208
|
+
arguments: [
|
|
209
|
+
{ name: 'description', description: 'Issue description', required: true },
|
|
210
|
+
],
|
|
211
|
+
}],
|
|
212
|
+
}));
|
|
213
|
+
|
|
214
|
+
server.setRequestHandler(GetPromptRequestSchema, async (req) => ({
|
|
215
|
+
messages: [{
|
|
216
|
+
role: 'user',
|
|
217
|
+
content: { type: 'text', text: `Triage this issue and suggest a priority:\n\n${req.params.arguments?.description}` },
|
|
218
|
+
}],
|
|
219
|
+
}));
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Transports
|
|
223
|
+
|
|
224
|
+
| Transport | Quando usar | Setup |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| **stdio** | Server local, agente spawnea processo | `StdioServerTransport` |
|
|
227
|
+
| **SSE** | Server remoto, streaming bidirectional | `SSEServerTransport` + Express/Fastify |
|
|
228
|
+
| **HTTP** | Server remoto stateless | endpoint POST que devolve JSON |
|
|
229
|
+
|
|
230
|
+
## Autorização por tool
|
|
231
|
+
|
|
232
|
+
Toda tool deve declarar:
|
|
233
|
+
- **Quem pode chamar** — escopo ou role
|
|
234
|
+
- **Quais argumentos validar** — não confiar no client
|
|
235
|
+
- **Side effects** — destrutivo? idempotente?
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// tool destrutiva exige confirmação ou role admin
|
|
239
|
+
if (input.confirm !== 'YES_I_AM_SURE') {
|
|
240
|
+
throw new Error('this tool requires confirmation');
|
|
241
|
+
}
|
|
242
|
+
if (!callerHasRole('admin')) {
|
|
243
|
+
throw new Error('admin only');
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Observabilidade
|
|
248
|
+
|
|
249
|
+
Logue cada tool call (sem dados sensíveis):
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
console.error(JSON.stringify({
|
|
253
|
+
ts: new Date().toISOString(),
|
|
254
|
+
tool: req.params.name,
|
|
255
|
+
duration_ms: Date.now() - start,
|
|
256
|
+
success: true,
|
|
257
|
+
// sem args (PII), sem result (PII)
|
|
258
|
+
}));
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
> stdio usa stdout para protocolo MCP — **logs sempre em stderr**.
|
|
262
|
+
|
|
263
|
+
## Testes
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// tests/create_issue.test.ts
|
|
267
|
+
import { test, expect, vi } from 'vitest';
|
|
268
|
+
import { CreateIssueInput } from '../src/tools/create_issue.js';
|
|
269
|
+
|
|
270
|
+
test('valida input correto', () => {
|
|
271
|
+
const valid = CreateIssueInput.parse({
|
|
272
|
+
title: 'Test',
|
|
273
|
+
team_id: '550e8400-e29b-41d4-a716-446655440000',
|
|
274
|
+
});
|
|
275
|
+
expect(valid.priority).toBe('medium'); // default
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('rejeita team_id inválido', () => {
|
|
279
|
+
expect(() => CreateIssueInput.parse({
|
|
280
|
+
title: 'Test',
|
|
281
|
+
team_id: 'not-a-uuid',
|
|
282
|
+
})).toThrow();
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
E2E com cliente MCP de teste:
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
290
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
291
|
+
|
|
292
|
+
const transport = new StdioClientTransport({ command: 'node', args: ['dist/index.js'] });
|
|
293
|
+
const client = new Client({ name: 'test', version: '0.0.0' }, { capabilities: {} });
|
|
294
|
+
await client.connect(transport);
|
|
295
|
+
|
|
296
|
+
const tools = await client.listTools();
|
|
297
|
+
expect(tools.tools.map(t => t.name)).toContain('create_issue');
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Antipatterns
|
|
301
|
+
|
|
302
|
+
| AP | Antipattern | Por quê |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| AP-01 | Logs no stdout (stdio) | Quebra o protocolo — sempre stderr |
|
|
305
|
+
| AP-02 | Tool sem schema de input | Cliente vai passar lixo |
|
|
306
|
+
| AP-03 | Sem validação no server | Confia no client (errado) |
|
|
307
|
+
| AP-04 | Tool destrutiva sem confirmação | Agente pode deletar dados sem querer |
|
|
308
|
+
| AP-05 | Secrets em código | Use env vars |
|
|
309
|
+
| AP-06 | Tool name não-descritivo (`do_thing`) | Agente não sabe quando usar |
|
|
310
|
+
| AP-07 | Description vago | Agente erra na escolha |
|
|
311
|
+
| AP-08 | Resource sem mimeType | Cliente não sabe parsear |
|
|
312
|
+
|
|
313
|
+
## Publicação
|
|
314
|
+
|
|
315
|
+
### Pré-requisitos para registry
|
|
316
|
+
|
|
317
|
+
- README.md com:
|
|
318
|
+
- Como instalar/configurar
|
|
319
|
+
- Lista de tools/resources/prompts
|
|
320
|
+
- Variáveis de ambiente necessárias
|
|
321
|
+
- Exemplo de `claude_desktop_config.json` (stdio)
|
|
322
|
+
- LICENSE (MIT recomendado)
|
|
323
|
+
- `package.json` ou `pyproject.toml` versionado
|
|
324
|
+
- Schema JSON exposto (vem das definições Zod/Pydantic)
|
|
325
|
+
|
|
326
|
+
### Distribuição
|
|
327
|
+
|
|
328
|
+
- **npm** — `npm publish` para servers TS
|
|
329
|
+
- **PyPI** — `python -m build && twine upload` para Python
|
|
330
|
+
- **Docker** — para SSE/HTTP servers (multi-stage build)
|
|
331
|
+
|
|
332
|
+
## Configuração no cliente (exemplo Claude Desktop)
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"mcpServers": {
|
|
337
|
+
"meu-server": {
|
|
338
|
+
"command": "node",
|
|
339
|
+
"args": ["/abs/path/to/dist/index.js"],
|
|
340
|
+
"env": {
|
|
341
|
+
"LINEAR_API_KEY": "lin_api_..."
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Como aplicar
|
|
349
|
+
|
|
350
|
+
### Passo 1: Decidir transport
|
|
351
|
+
|
|
352
|
+
Local + uso pessoal → **stdio**. Multi-usuário ou cloud → **SSE/HTTP**.
|
|
353
|
+
|
|
354
|
+
### Passo 2: Listar tools/resources/prompts
|
|
355
|
+
|
|
356
|
+
Inventarie no DESIGN.md o que vai expor. Cada tool: nome, descrição clara, input schema, output schema.
|
|
357
|
+
|
|
358
|
+
### Passo 3: Scaffold
|
|
359
|
+
|
|
360
|
+
Use SDK oficial. Estruture em `tools/`, `resources/`, `prompts/`.
|
|
361
|
+
|
|
362
|
+
### Passo 4: Schemas Zod/Pydantic
|
|
363
|
+
|
|
364
|
+
Toda tool tem schema de input. Output validado antes de retornar.
|
|
365
|
+
|
|
366
|
+
### Passo 5: Logs em stderr + observabilidade
|
|
367
|
+
|
|
368
|
+
`console.error` (stdio) com JSON estruturado.
|
|
369
|
+
|
|
370
|
+
### Passo 6: Testes + publicação
|
|
371
|
+
|
|
372
|
+
Vitest/pytest. README + LICENSE MIT. Publicar npm/PyPI.
|
|
373
|
+
|
|
374
|
+
## Dicas
|
|
375
|
+
|
|
376
|
+
- **Combine** com `dare-llm-integration` se o server chama LLM
|
|
377
|
+
- **Use** `dare-security` para auditar dependências
|
|
378
|
+
- **Para tools destrutivas**, exija confirm flag explícito
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
Esta skill é parte do DARE Method e está sob licença MIT.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skill-nestjs-api
|
|
3
|
+
description: Padrões DARE para APIs REST em NestJS + TypeScript + Prisma + Swagger. Modules, Controllers, Services, DTOs com class-validator, Guards, Interceptors, exceções globais, Jest + Supertest, OpenAPI auto-gerado, rate limiting com @nestjs/throttler.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DARE NestJS API Skill
|
|
7
|
+
|
|
8
|
+
Você é um desenvolvedor sênior TypeScript / NestJS especializado em APIs REST. Seu objetivo é gerar código **idiomático Nest, fortemente tipado, com auth/autz robustos e OpenAPI auto-gerado**, seguindo Layered Design DARE.
|
|
9
|
+
|
|
10
|
+
## Quando usar
|
|
11
|
+
|
|
12
|
+
- Projeto NestJS novo via DARE
|
|
13
|
+
- Adicionar feature em API NestJS existente
|
|
14
|
+
- Migrar API Express clássica para NestJS
|
|
15
|
+
- Auditar projeto NestJS para conformidade DARE
|
|
16
|
+
|
|
17
|
+
## Stack canônica
|
|
18
|
+
|
|
19
|
+
- **TypeScript 5.5+** com `strict: true`
|
|
20
|
+
- **NestJS 11.x** (módulos, providers, DI)
|
|
21
|
+
- **Prisma 5.x** ORM (PostgreSQL ou MySQL)
|
|
22
|
+
- **class-validator + class-transformer** para DTOs
|
|
23
|
+
- **@nestjs/swagger** para OpenAPI auto-gerado
|
|
24
|
+
- **@nestjs/throttler** para rate limiting
|
|
25
|
+
- **@nestjs/passport + @nestjs/jwt** para auth
|
|
26
|
+
- **Jest + Supertest** para testes
|
|
27
|
+
- **ESLint + Prettier** para formatação
|
|
28
|
+
|
|
29
|
+
## Layered Design em NestJS
|
|
30
|
+
|
|
31
|
+
Cada módulo segue:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
src/<feature>/
|
|
35
|
+
├── <feature>.module.ts ← define providers, controllers
|
|
36
|
+
├── <feature>.controller.ts ← Handler
|
|
37
|
+
├── <feature>.service.ts ← Service (uma operação) ou facade
|
|
38
|
+
├── <feature>.repository.ts ← Repository (Prisma)
|
|
39
|
+
├── dto/
|
|
40
|
+
│ ├── create-<feature>.dto.ts ← class-validator
|
|
41
|
+
│ └── update-<feature>.dto.ts
|
|
42
|
+
├── entities/
|
|
43
|
+
│ └── <feature>.entity.ts ← Model
|
|
44
|
+
└── tests/
|
|
45
|
+
├── <feature>.service.spec.ts ← unit
|
|
46
|
+
└── <feature>.e2e-spec.ts ← E2E
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Controllers (Handler)
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
|
|
53
|
+
import { ApiTags, ApiOperation, ApiCreatedResponse } from '@nestjs/swagger';
|
|
54
|
+
import { JwtAuthGuard } from '@/auth/jwt-auth.guard';
|
|
55
|
+
import { CreateUserDto } from './dto/create-user.dto';
|
|
56
|
+
import { UserResponseDto } from './dto/user-response.dto';
|
|
57
|
+
import { RegisterUserService } from './register-user.service';
|
|
58
|
+
|
|
59
|
+
@ApiTags('users')
|
|
60
|
+
@Controller('users')
|
|
61
|
+
@UseGuards(JwtAuthGuard)
|
|
62
|
+
export class UsersController {
|
|
63
|
+
constructor(private readonly register: RegisterUserService) {}
|
|
64
|
+
|
|
65
|
+
@Post()
|
|
66
|
+
@ApiOperation({ summary: 'Criar novo usuário' })
|
|
67
|
+
@ApiCreatedResponse({ type: UserResponseDto })
|
|
68
|
+
async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
|
|
69
|
+
const user = await this.register.execute(dto);
|
|
70
|
+
return UserResponseDto.from(user);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## DTOs (validação)
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { IsEmail, IsString, MinLength } from 'class-validator';
|
|
79
|
+
import { ApiProperty } from '@nestjs/swagger';
|
|
80
|
+
|
|
81
|
+
export class CreateUserDto {
|
|
82
|
+
@ApiProperty({ example: 'jane@example.com' })
|
|
83
|
+
@IsEmail()
|
|
84
|
+
email!: string;
|
|
85
|
+
|
|
86
|
+
@ApiProperty({ example: 'Jane Doe' })
|
|
87
|
+
@IsString()
|
|
88
|
+
name!: string;
|
|
89
|
+
|
|
90
|
+
@ApiProperty({ minLength: 12 })
|
|
91
|
+
@IsString()
|
|
92
|
+
@MinLength(12)
|
|
93
|
+
password!: string;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`main.ts` ativa global pipe:
|
|
98
|
+
```typescript
|
|
99
|
+
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }));
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Services (Business Logic)
|
|
103
|
+
|
|
104
|
+
Uma operação por classe:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { Injectable } from '@nestjs/common';
|
|
108
|
+
import { hash } from '@node-rs/argon2';
|
|
109
|
+
import { UsersRepository } from './users.repository';
|
|
110
|
+
import { CreateUserDto } from './dto/create-user.dto';
|
|
111
|
+
import { User } from './entities/user.entity';
|
|
112
|
+
import { UserAlreadyExistsError } from './errors';
|
|
113
|
+
|
|
114
|
+
@Injectable()
|
|
115
|
+
export class RegisterUserService {
|
|
116
|
+
constructor(private readonly repo: UsersRepository) {}
|
|
117
|
+
|
|
118
|
+
async execute(dto: CreateUserDto): Promise<User> {
|
|
119
|
+
if (await this.repo.existsByEmail(dto.email)) {
|
|
120
|
+
throw new UserAlreadyExistsError();
|
|
121
|
+
}
|
|
122
|
+
return this.repo.create({
|
|
123
|
+
...dto,
|
|
124
|
+
password: await hash(dto.password),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Repositories (Prisma)
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { Injectable } from '@nestjs/common';
|
|
134
|
+
import { PrismaService } from '@/prisma/prisma.service';
|
|
135
|
+
|
|
136
|
+
@Injectable()
|
|
137
|
+
export class UsersRepository {
|
|
138
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
139
|
+
|
|
140
|
+
async existsByEmail(email: string): Promise<boolean> {
|
|
141
|
+
return !!(await this.prisma.user.findUnique({ where: { email } }));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async create(data: { email: string; name: string; password: string }) {
|
|
145
|
+
return this.prisma.user.create({ data });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Tratamento global de exceções
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// src/common/filters/all-exceptions.filter.ts
|
|
154
|
+
@Catch()
|
|
155
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
156
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
157
|
+
const ctx = host.switchToHttp();
|
|
158
|
+
const response = ctx.getResponse<Response>();
|
|
159
|
+
|
|
160
|
+
if (exception instanceof UserAlreadyExistsError) {
|
|
161
|
+
return response.status(409).json({ error: 'USER_EXISTS' });
|
|
162
|
+
}
|
|
163
|
+
if (exception instanceof HttpException) {
|
|
164
|
+
return response.status(exception.getStatus()).json(exception.getResponse());
|
|
165
|
+
}
|
|
166
|
+
// log + 500 genérico
|
|
167
|
+
return response.status(500).json({ error: 'INTERNAL' });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// main.ts
|
|
172
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## OpenAPI auto-gerado
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// main.ts
|
|
179
|
+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
180
|
+
|
|
181
|
+
const config = new DocumentBuilder()
|
|
182
|
+
.setTitle('Projeto API')
|
|
183
|
+
.setVersion('1.0')
|
|
184
|
+
.addBearerAuth()
|
|
185
|
+
.build();
|
|
186
|
+
|
|
187
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
188
|
+
SwaggerModule.setup('docs', app, document); // UI em /docs
|
|
189
|
+
writeFileSync('./public/openapi.json', JSON.stringify(document));
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Rate limiting
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { ThrottlerModule } from '@nestjs/throttler';
|
|
196
|
+
|
|
197
|
+
@Module({
|
|
198
|
+
imports: [
|
|
199
|
+
ThrottlerModule.forRoot([{ ttl: 60_000, limit: 100 }]),
|
|
200
|
+
],
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// Endpoint sensível
|
|
204
|
+
@Throttle({ default: { limit: 5, ttl: 900_000 } }) // 5/15min
|
|
205
|
+
@Post('login')
|
|
206
|
+
async login() {...}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Auth (JWT + Passport)
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
@Injectable()
|
|
213
|
+
export class JwtAuthGuard extends AuthGuard('jwt') {}
|
|
214
|
+
|
|
215
|
+
@Injectable()
|
|
216
|
+
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
217
|
+
constructor(config: ConfigService) {
|
|
218
|
+
super({
|
|
219
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
220
|
+
secretOrKey: config.getOrThrow('JWT_SECRET'),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
async validate(payload: any) {
|
|
224
|
+
return { id: payload.sub, email: payload.email };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Testes
|
|
230
|
+
|
|
231
|
+
### Unit (service)
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
describe('RegisterUserService', () => {
|
|
235
|
+
it('falha se email já existe', async () => {
|
|
236
|
+
const repo = { existsByEmail: jest.fn().mockResolvedValue(true) } as any;
|
|
237
|
+
const sut = new RegisterUserService(repo);
|
|
238
|
+
await expect(sut.execute({ email: 'x@y.com', name: 'X', password: 'longsecret123' }))
|
|
239
|
+
.rejects.toThrow(UserAlreadyExistsError);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### E2E (Supertest)
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
describe('POST /users', () => {
|
|
248
|
+
it('cria usuário com sucesso', async () => {
|
|
249
|
+
const res = await request(app.getHttpServer())
|
|
250
|
+
.post('/users')
|
|
251
|
+
.set('Authorization', `Bearer ${adminToken}`)
|
|
252
|
+
.send({ email: 'jane@example.com', name: 'Jane', password: 'longsecret123' });
|
|
253
|
+
expect(res.status).toBe(201);
|
|
254
|
+
expect(res.body.email).toBe('jane@example.com');
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Antipatterns
|
|
260
|
+
|
|
261
|
+
| AP | Antipattern | Correção |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| AP-01 | Validação inline no Controller | DTO + ValidationPipe |
|
|
264
|
+
| AP-02 | Prisma direto no Controller | Repository |
|
|
265
|
+
| AP-03 | Lógica no Controller | Service |
|
|
266
|
+
| AP-04 | Sem DTO de saída | `UserResponseDto.from(user)` |
|
|
267
|
+
| AP-05 | `JwtStrategy` com secret hardcoded | `ConfigService.getOrThrow` |
|
|
268
|
+
| AP-06 | Sem `@nestjs/throttler` em login | rate limit obrigatório |
|
|
269
|
+
| AP-07 | OpenAPI escrito à mão | `@nestjs/swagger` decorators |
|
|
270
|
+
| AP-08 | Erros sem filter global | inconsistência de response |
|
|
271
|
+
|
|
272
|
+
## Segurança (combinar com `dare-security`)
|
|
273
|
+
|
|
274
|
+
- Hash com `@node-rs/argon2` (Argon2id)
|
|
275
|
+
- JWT: RS256 para tokens públicos, HS256 + secret ≥256 bits para internos
|
|
276
|
+
- `helmet` middleware para headers
|
|
277
|
+
- CORS específico, nunca `*`
|
|
278
|
+
- Rate limit em login (5/15min) + APIs públicas
|
|
279
|
+
- Refresh token com rotação no DB
|
|
280
|
+
|
|
281
|
+
## Validação no CI
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
npm run lint
|
|
285
|
+
npm run test
|
|
286
|
+
npm run test:e2e
|
|
287
|
+
npm run build
|
|
288
|
+
npx prisma migrate deploy --dry-run # valida migrations
|
|
289
|
+
npm audit --audit-level=high
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Como aplicar
|
|
293
|
+
|
|
294
|
+
### Passo 1: Audit
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
grep -rn "@Body()" src/ | grep -v "Dto" # AP-01
|
|
298
|
+
grep -rn "prisma\." src/*/controllers/ # AP-02
|
|
299
|
+
grep -rn "JwtStrategy" src/ | grep -v Config # AP-05
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Passo 2: Migrar para DTOs
|
|
303
|
+
|
|
304
|
+
Para cada `@Body() body: any`, criar DTO com class-validator + `@ApiProperty`.
|
|
305
|
+
|
|
306
|
+
### Passo 3: Extrair Services
|
|
307
|
+
|
|
308
|
+
Toda lógica > 5 linhas em Controller → Service injetável.
|
|
309
|
+
|
|
310
|
+
### Passo 4: Adicionar Repositories
|
|
311
|
+
|
|
312
|
+
Encapsular Prisma. Controller chama Service, Service chama Repository.
|
|
313
|
+
|
|
314
|
+
### Passo 5: Configurar Swagger + throttler
|
|
315
|
+
|
|
316
|
+
`main.ts` com `SwaggerModule.setup` e exportar `public/openapi.json`. Adicionar `ThrottlerModule` global.
|
|
317
|
+
|
|
318
|
+
## Dicas
|
|
319
|
+
|
|
320
|
+
- **Combine** com `dare-docker` para PHP-FPM-style separação (Node + Postgres + Redis)
|
|
321
|
+
- **Use** `dare-llm-integration` se houver chamadas LLM
|
|
322
|
+
- **Para realtime**, use `@nestjs/websockets` + skill `dare-realtime`
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
Esta skill é parte do DARE Method e está sob licença MIT.
|