@polymorphism-tech/morph-spec 8.38.0 → 8.40.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/CLAUDE.md +19 -8
- package/README.md +2 -2
- package/bin/morph-spec.js +60 -43
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +3 -2
- package/framework/MORPH.md +1 -1
- package/framework/agents.json +36 -25
- package/framework/docs/CLI.md +2 -2
- package/framework/evals/rubrics/code-quality.md +4 -4
- package/framework/evals/rubrics/condensed.md +2 -2
- package/framework/mcp/registry.json +33 -2
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +9 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +5 -1
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +6 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +4 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +6 -5
- package/framework/standards/STANDARDS.json +3 -3
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/architecture/code-comments.md +52 -16
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +48 -117
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/project/doctor.js +3 -5
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +5 -0
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +59 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +2 -0
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +22 -0
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +32 -4
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +2 -4
- package/src/utils/agents-installer.js +57 -12
- package/src/utils/banner.js +1 -1
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
|
@@ -125,16 +125,19 @@ real sign-in attempt, not after debugging a mysterious "session never sticks" bu
|
|
|
125
125
|
|
|
126
126
|
### SDK Initialization
|
|
127
127
|
|
|
128
|
+
`lib/auth/client.ts` — `createAuthClient` takes the public auth URL as a positional argument, and the
|
|
129
|
+
React hooks are re-exported from the client instance for ergonomic imports:
|
|
130
|
+
|
|
128
131
|
```typescript
|
|
129
|
-
// lib/auth/client.ts
|
|
130
132
|
'use client';
|
|
131
133
|
import { createAuthClient } from '@neondatabase/auth/next';
|
|
132
|
-
// createAuthClient takes the public auth URL as a positional argument
|
|
133
134
|
export const authClient = createAuthClient(process.env.NEXT_PUBLIC_AUTH_URL!);
|
|
134
|
-
// Re-export the React hooks from the client instance for ergonomic imports
|
|
135
135
|
export const { useSession } = authClient;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`lib/auth/server.ts`:
|
|
136
139
|
|
|
137
|
-
|
|
140
|
+
```typescript
|
|
138
141
|
import { createNeonAuth } from '@neondatabase/auth/next/server';
|
|
139
142
|
export const auth = createNeonAuth({
|
|
140
143
|
baseUrl: process.env.NEON_AUTH_BASE_URL!,
|
|
@@ -152,8 +155,9 @@ Import the Tailwind styles for the UI components in `globals.css`:
|
|
|
152
155
|
@import '@neondatabase/auth-ui/tailwind';
|
|
153
156
|
```
|
|
154
157
|
|
|
158
|
+
`app/providers.tsx`:
|
|
159
|
+
|
|
155
160
|
```tsx
|
|
156
|
-
// app/providers.tsx
|
|
157
161
|
'use client';
|
|
158
162
|
import { NeonAuthUIProvider } from '@neondatabase/auth-ui';
|
|
159
163
|
import { authClient } from '@/lib/auth/client';
|
|
@@ -169,8 +173,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
169
173
|
|
|
170
174
|
### Auth API Route
|
|
171
175
|
|
|
176
|
+
`app/api/auth/[...path]/route.ts`:
|
|
177
|
+
|
|
172
178
|
```typescript
|
|
173
|
-
// app/api/auth/[...path]/route.ts
|
|
174
179
|
import { auth } from '@/lib/auth/server';
|
|
175
180
|
|
|
176
181
|
export const { GET, POST } = auth.handler();
|
|
@@ -182,8 +187,9 @@ export const { GET, POST } = auth.handler();
|
|
|
182
187
|
|
|
183
188
|
### Sign In / Sign Up Pages
|
|
184
189
|
|
|
190
|
+
`app/auth/sign-in/page.tsx`:
|
|
191
|
+
|
|
185
192
|
```tsx
|
|
186
|
-
// app/auth/sign-in/page.tsx
|
|
187
193
|
import { AuthView } from '@neondatabase/auth-ui';
|
|
188
194
|
|
|
189
195
|
export default function SignInPage() {
|
|
@@ -193,8 +199,11 @@ export default function SignInPage() {
|
|
|
193
199
|
</div>
|
|
194
200
|
);
|
|
195
201
|
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`app/auth/sign-up/page.tsx`:
|
|
196
205
|
|
|
197
|
-
|
|
206
|
+
```tsx
|
|
198
207
|
import { AuthView } from '@neondatabase/auth-ui';
|
|
199
208
|
|
|
200
209
|
export default function SignUpPage() {
|
|
@@ -208,8 +217,9 @@ export default function SignUpPage() {
|
|
|
208
217
|
|
|
209
218
|
### Server-Side Auth Check
|
|
210
219
|
|
|
220
|
+
`app/dashboard/page.tsx`:
|
|
221
|
+
|
|
211
222
|
```typescript
|
|
212
|
-
// app/dashboard/page.tsx
|
|
213
223
|
import { auth } from '@/lib/auth/server';
|
|
214
224
|
import { redirect } from 'next/navigation';
|
|
215
225
|
|
|
@@ -228,8 +238,9 @@ export default async function DashboardPage() {
|
|
|
228
238
|
|
|
229
239
|
### Client Component with User Data
|
|
230
240
|
|
|
241
|
+
`components/UserProfile.tsx`:
|
|
242
|
+
|
|
231
243
|
```tsx
|
|
232
|
-
// components/UserProfile.tsx
|
|
233
244
|
'use client';
|
|
234
245
|
import { useSession } from '@/lib/auth/client';
|
|
235
246
|
|
|
@@ -249,14 +260,15 @@ export function UserProfile() {
|
|
|
249
260
|
|
|
250
261
|
### Route Protection (Middleware)
|
|
251
262
|
|
|
263
|
+
`middleware.ts`. The `runtime = 'nodejs'` line is **mandatory** for any `middleware.ts` that touches
|
|
264
|
+
Neon Auth: Next.js middleware runs on the Edge runtime by default, and the network call the Neon Auth
|
|
265
|
+
SDK makes to mint the session-data cookie times out inside the Edge sandbox on `next dev` — the request
|
|
266
|
+
silently bounces back to sign-in even with a fully valid session, with no error surfaced.
|
|
267
|
+
|
|
252
268
|
```typescript
|
|
253
|
-
// middleware.ts
|
|
254
269
|
import { auth } from '@/lib/auth/server';
|
|
255
270
|
|
|
256
|
-
//
|
|
257
|
-
// runtime by default, and the network call the Neon Auth SDK makes to mint the session-data cookie
|
|
258
|
-
// times out inside the Edge sandbox on `next dev` — the request silently bounces back to sign-in
|
|
259
|
-
// even with a fully valid session, with no error surfaced. Force the Node.js runtime instead.
|
|
271
|
+
// Mandatory: on the Edge runtime the SDK's session-cookie call times out and silently bounces to sign-in.
|
|
260
272
|
export const runtime = 'nodejs';
|
|
261
273
|
|
|
262
274
|
export default auth.middleware({
|
|
@@ -294,7 +306,6 @@ export default function ProtectedPage() {
|
|
|
294
306
|
## OAuth (Social Login)
|
|
295
307
|
|
|
296
308
|
```typescript
|
|
297
|
-
// Trigger OAuth sign-in
|
|
298
309
|
await authClient.signIn.social({
|
|
299
310
|
provider: 'google',
|
|
300
311
|
callbackURL: 'http://localhost:3000/auth/callback',
|
|
@@ -307,8 +318,9 @@ Configure OAuth providers in the Neon Console under Auth > Configuration.
|
|
|
307
318
|
|
|
308
319
|
## API Route Protection
|
|
309
320
|
|
|
321
|
+
`app/api/protected/route.ts`:
|
|
322
|
+
|
|
310
323
|
```typescript
|
|
311
|
-
// app/api/protected/route.ts
|
|
312
324
|
import { auth } from '@/lib/auth/server';
|
|
313
325
|
|
|
314
326
|
export const dynamic = 'force-dynamic';
|
|
@@ -348,34 +360,38 @@ backend's real prefix and the proxy's public prefix don't happen to match charac
|
|
|
348
360
|
because this is a runtime routing mismatch (404, or the proxy calling itself), it **never shows up at
|
|
349
361
|
build time**, only when a human clicks the page.
|
|
350
362
|
|
|
363
|
+
`lib/api/prefixes.ts` — `BACKEND_API_PREFIX` is used ONLY by Server Components / Route Handlers
|
|
364
|
+
calling the .NET backend directly (e.g. `https://api.example.com`; the backend's own routes have NO
|
|
365
|
+
`/api` prefix); `PROXY_API_PREFIX` is used ONLY by Client Components calling the same-origin Next.js
|
|
366
|
+
proxy, and matches `app/api/proxy/[...path]/route.ts`:
|
|
367
|
+
|
|
351
368
|
```typescript
|
|
352
|
-
|
|
353
|
-
// BACKEND_API_PREFIX: used ONLY by Server Components / Route Handlers calling the .NET backend directly.
|
|
354
|
-
export const BACKEND_API_PREFIX = process.env.API_URL; // e.g. https://api.example.com — backend's own routes have NO /api prefix
|
|
369
|
+
export const BACKEND_API_PREFIX = process.env.API_URL;
|
|
355
370
|
|
|
356
|
-
|
|
357
|
-
export const PROXY_API_PREFIX = '/api/proxy'; // matches app/api/proxy/[...path]/route.ts
|
|
371
|
+
export const PROXY_API_PREFIX = '/api/proxy';
|
|
358
372
|
```
|
|
359
373
|
|
|
374
|
+
The two call sites below — `app/(dashboard)/catalog/page.tsx`, a Server Component calling the backend
|
|
375
|
+
directly, and `features/catalog/hooks/use-items.ts`, a Client Component going through the same-origin
|
|
376
|
+
proxy — MUST resolve to the same backend route:
|
|
377
|
+
|
|
360
378
|
```typescript
|
|
361
|
-
// app/(dashboard)/catalog/page.tsx — Server Component, direct backend call
|
|
362
379
|
import { BACKEND_API_PREFIX } from '@/lib/api/prefixes';
|
|
363
380
|
|
|
364
381
|
async function getItems() {
|
|
365
|
-
// cross-reference: PROXY_API_PREFIX below MUST resolve to the same backend route
|
|
366
382
|
const res = await fetch(`${BACKEND_API_PREFIX}/catalog/items`, { next: { revalidate: 60 } });
|
|
367
383
|
return res.json();
|
|
368
384
|
}
|
|
369
385
|
```
|
|
370
386
|
|
|
387
|
+
`features/catalog/hooks/use-items.ts`:
|
|
388
|
+
|
|
371
389
|
```typescript
|
|
372
|
-
// features/catalog/hooks/use-items.ts — Client Component, via same-origin proxy
|
|
373
390
|
import { PROXY_API_PREFIX } from '@/lib/api/prefixes';
|
|
374
391
|
|
|
375
392
|
export function useItems() {
|
|
376
393
|
return useQuery({
|
|
377
394
|
queryKey: ['catalog', 'items'],
|
|
378
|
-
// cross-reference: BACKEND_API_PREFIX above MUST resolve to the same backend route
|
|
379
395
|
queryFn: async () => (await fetch(`${PROXY_API_PREFIX}/catalog/items`)).json(),
|
|
380
396
|
});
|
|
381
397
|
}
|
|
@@ -435,31 +451,30 @@ Before marking any task that adds or touches an endpoint as done:
|
|
|
435
451
|
|
|
436
452
|
### JWT Bearer Configuration
|
|
437
453
|
|
|
454
|
+
`Infrastructure/Auth/NeonAuthOptions.cs` — `BaseUrl` is the Neon Auth Base URL, e.g.
|
|
455
|
+
`https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth`:
|
|
456
|
+
|
|
438
457
|
```csharp
|
|
439
|
-
// Infrastructure/Auth/NeonAuthOptions.cs
|
|
440
458
|
namespace {ProjectName}.Infrastructure.Auth;
|
|
441
459
|
|
|
442
460
|
public sealed class NeonAuthOptions
|
|
443
461
|
{
|
|
444
462
|
public const string SectionName = "NeonAuth";
|
|
445
463
|
|
|
446
|
-
/// <summary>Neon Auth Base URL, e.g. https://ep-xxx.neonauth.us-east-2.aws.neon.build/neondb/auth</summary>
|
|
447
464
|
public string BaseUrl { get; set; } = string.Empty;
|
|
448
465
|
}
|
|
449
466
|
```
|
|
450
467
|
|
|
468
|
+
`Infrastructure/Auth/NeonAuthJwksProvider.cs` fetches and caches Neon Auth's raw JWKS
|
|
469
|
+
(`/.well-known/jwks.json` — there is no OIDC discovery document) for 15 minutes; an unknown `kid`
|
|
470
|
+
forces one refresh, to survive key rotation without waiting out the TTL:
|
|
471
|
+
|
|
451
472
|
```csharp
|
|
452
|
-
// Infrastructure/Auth/NeonAuthJwksProvider.cs
|
|
453
473
|
namespace {ProjectName}.Infrastructure.Auth;
|
|
454
474
|
|
|
455
475
|
using Microsoft.Extensions.Options;
|
|
456
476
|
using Microsoft.IdentityModel.Tokens;
|
|
457
477
|
|
|
458
|
-
/// <summary>
|
|
459
|
-
/// Fetches and caches Neon Auth's raw JWKS (/.well-known/jwks.json — there is no OIDC discovery
|
|
460
|
-
/// document). Cached for 15 minutes; a forced refresh happens once when an unknown `kid` is seen,
|
|
461
|
-
/// to survive key rotation without waiting out the TTL.
|
|
462
|
-
/// </summary>
|
|
463
478
|
public sealed class NeonAuthJwksProvider(HttpClient httpClient, IOptions<NeonAuthOptions> options)
|
|
464
479
|
{
|
|
465
480
|
private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(15);
|
|
@@ -473,7 +488,7 @@ public sealed class NeonAuthJwksProvider(HttpClient httpClient, IOptions<NeonAut
|
|
|
473
488
|
var key = keySet.Keys.FirstOrDefault(k => k.Kid == kid);
|
|
474
489
|
if (key is not null) return key;
|
|
475
490
|
|
|
476
|
-
// Unknown kid
|
|
491
|
+
// Unknown kid may be key rotation: refresh once before giving up.
|
|
477
492
|
keySet = await GetKeySetAsync(forceRefresh: true, cancellationToken);
|
|
478
493
|
return keySet.Keys.FirstOrDefault(k => k.Kid == kid);
|
|
479
494
|
}
|
|
@@ -503,8 +518,9 @@ public sealed class NeonAuthJwksProvider(HttpClient httpClient, IOptions<NeonAut
|
|
|
503
518
|
}
|
|
504
519
|
```
|
|
505
520
|
|
|
521
|
+
`Infrastructure/Auth/NeonAuthServiceExtensions.cs`:
|
|
522
|
+
|
|
506
523
|
```csharp
|
|
507
|
-
// Infrastructure/Auth/NeonAuthServiceExtensions.cs
|
|
508
524
|
namespace {ProjectName}.Infrastructure.Auth;
|
|
509
525
|
|
|
510
526
|
using System.Text;
|
|
@@ -527,9 +543,7 @@ public static class NeonAuthServiceExtensions
|
|
|
527
543
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
528
544
|
.AddJwtBearer(options =>
|
|
529
545
|
{
|
|
530
|
-
//
|
|
531
|
-
// so Authority-driven metadata refresh has nothing valid to fetch. ValidIssuer is set
|
|
532
|
-
// explicitly below instead.
|
|
546
|
+
// No options.Authority: Neon Auth serves no OIDC discovery document, so ValidIssuer is explicit.
|
|
533
547
|
options.TokenValidationParameters = new TokenValidationParameters
|
|
534
548
|
{
|
|
535
549
|
ValidateIssuer = true,
|
|
@@ -537,14 +551,12 @@ public static class NeonAuthServiceExtensions
|
|
|
537
551
|
ValidateAudience = false,
|
|
538
552
|
ValidateLifetime = true,
|
|
539
553
|
NameClaimType = "sub",
|
|
540
|
-
//
|
|
541
|
-
// below (fetched from our own cached JWKS) — this flag doesn't gate that path.
|
|
554
|
+
// Key trust is established inside SignatureValidator below; this flag doesn't gate that path.
|
|
542
555
|
ValidateIssuerSigningKey = false,
|
|
543
556
|
};
|
|
544
557
|
});
|
|
545
558
|
|
|
546
|
-
//
|
|
547
|
-
// so this only *adds* the SignatureValidator to the TokenValidationParameters built above.
|
|
559
|
+
// Runs after AddJwtBearer's own setup, with DI available: it only adds the SignatureValidator.
|
|
548
560
|
services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
|
|
549
561
|
.Configure<NeonAuthJwksProvider>((options, jwks) =>
|
|
550
562
|
{
|
|
@@ -561,9 +573,7 @@ public static class NeonAuthServiceExtensions
|
|
|
561
573
|
if (string.IsNullOrEmpty(kid))
|
|
562
574
|
throw new SecurityTokenInvalidSignatureException("Token is missing 'kid'.");
|
|
563
575
|
|
|
564
|
-
// Blocking
|
|
565
|
-
// I/O-free on the hot path. SignatureValidator has no async overload in this
|
|
566
|
-
// library version — only a cold-start / key-rotation miss ever awaits real I/O here.
|
|
576
|
+
// Blocking is safe: SignatureValidator has no async overload and the JWKS is cached in memory.
|
|
567
577
|
var jwk = jwks.GetKeyAsync(kid, CancellationToken.None).GetAwaiter().GetResult()
|
|
568
578
|
?? throw new SecurityTokenInvalidSignatureException($"No JWKS key found for kid '{kid}'.");
|
|
569
579
|
|
|
@@ -585,10 +595,7 @@ public static class NeonAuthServiceExtensions
|
|
|
585
595
|
if (!verifier.VerifySignature(signature))
|
|
586
596
|
throw new SecurityTokenInvalidSignatureException("Ed25519 signature verification failed.");
|
|
587
597
|
|
|
588
|
-
//
|
|
589
|
-
// uses JsonWebTokenHandler internally. Returning a
|
|
590
|
-
// System.IdentityModel.Tokens.Jwt.JwtSecurityToken here fails with IDX10506
|
|
591
|
-
// ("SignatureValidator returned a token of an unexpected type").
|
|
598
|
+
// Must be JsonWebToken: ASP.NET Core 8+ rejects a JwtSecurityToken here with IDX10506.
|
|
592
599
|
return jwt;
|
|
593
600
|
};
|
|
594
601
|
});
|
|
@@ -600,6 +607,12 @@ public static class NeonAuthServiceExtensions
|
|
|
600
607
|
}
|
|
601
608
|
```
|
|
602
609
|
|
|
610
|
+
Two details of the validator above: it returns `Microsoft.IdentityModel.JsonWebTokens.JsonWebToken`
|
|
611
|
+
because ASP.NET Core 8+ uses `JsonWebTokenHandler` internally — returning a
|
|
612
|
+
`System.IdentityModel.Tokens.Jwt.JwtSecurityToken` fails with IDX10506 ("SignatureValidator returned a
|
|
613
|
+
token of an unexpected type"); and the blocking `GetResult()` is I/O-free on the hot path, since only a
|
|
614
|
+
cold-start or key-rotation miss ever awaits real I/O.
|
|
615
|
+
|
|
603
616
|
### Protected Endpoints
|
|
604
617
|
|
|
605
618
|
```csharp
|
|
@@ -43,7 +43,7 @@ Skipping this chain is the most common cause of a page whose images do not feel
|
|
|
43
43
|
|
|
44
44
|
```javascript
|
|
45
45
|
// Generate a new image
|
|
46
|
-
|
|
46
|
+
mcp__gemini-image-mcp__generate_image({
|
|
47
47
|
prompt: string, // Text description (max 5000 chars, narrative style)
|
|
48
48
|
aspectRatio?: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "9:16" | "16:9",
|
|
49
49
|
outputPath?: string, // File path to save (e.g., "public/images/feature/hero.png")
|
|
@@ -51,25 +51,25 @@ mcp__gemini_image_mcp__generate_image({
|
|
|
51
51
|
})
|
|
52
52
|
|
|
53
53
|
// Edit an existing image
|
|
54
|
-
|
|
54
|
+
mcp__gemini-image-mcp__edit_image({
|
|
55
55
|
imageSource: string, // Path to image or image ID
|
|
56
56
|
prompt: string, // Short, targeted edit instructions (1-2 sentences)
|
|
57
57
|
outputPath?: string,
|
|
58
58
|
})
|
|
59
59
|
|
|
60
60
|
// Continue editing last image
|
|
61
|
-
|
|
61
|
+
mcp__gemini-image-mcp__continue_editing({
|
|
62
62
|
prompt: string, // One adjustment at a time
|
|
63
63
|
})
|
|
64
64
|
|
|
65
65
|
// List generated images
|
|
66
|
-
|
|
66
|
+
mcp__gemini-image-mcp__list_images({
|
|
67
67
|
type?: "generated" | "edited" | "all",
|
|
68
68
|
limit?: number,
|
|
69
69
|
})
|
|
70
70
|
|
|
71
71
|
// Get image details
|
|
72
|
-
|
|
72
|
+
mcp__gemini-image-mcp__get_image_info({
|
|
73
73
|
imageId?: string, // Defaults to last image
|
|
74
74
|
})
|
|
75
75
|
```
|
|
@@ -5,32 +5,26 @@
|
|
|
5
5
|
> **Keywords:** mcp, model-context-protocol, mcp-tools, context7, playwright, mcp-client
|
|
6
6
|
> **Load When:** an MCP tool is used in any morph-spec phase
|
|
7
7
|
|
|
8
|
-
**Verified against:** Claude Code MCP tooling — context7, Playwright; GitHub via `gh` CLI; Neon via `neonctl`. Last-verified: 2026-
|
|
8
|
+
**Verified against:** Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl`. Last-verified: 2026-09-24.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
Reference for using Model Context Protocol (MCP) tools across morph-spec phases. Each phase benefits from different MCPs depending on what data is needed.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Tool Names Depend on Where the Server Is Configured
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
The names below use the `.mcp.json` form, `mcp__<server>__<tool>`. The same server installed as a **plugin** is named `mcp__plugin_<plugin>_<server>__<tool>`: context7 from the plugin is `mcp__plugin_context7_context7__query-docs`, not `mcp__context7__query-docs`. Call the name that exists in your session. The tool part (`query-docs`, `browser_navigate`) is the same under both prefixes.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
In a morph persona the grant follows the same rule: the installer emits one `mcp__<name>` per spelling in the registry's `serverNames`, and a grant matches only its own spelling. If a tool you expect is missing, it may be deferred: load it with `ToolSearch` before calling it.
|
|
19
|
+
|
|
20
|
+
## MCP Availability Detection
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
// gh repo view --json name,defaultBranchRef,languages
|
|
22
|
+
Before using any MCP tool, check if it's available in the current session. There is no "list MCPs" command: attempt the most common operation, and Claude Code reports a tool that doesn't exist. GitHub and Neon are not checked this way — o morph-spec NÃO usa MCP do GitHub nem do Neon: use o `gh` CLI (`gh repo view --json name,defaultBranchRef,languages`) e o `neonctl` + `psql` (`neonctl connection-string` dá a URL do banco) via Bash.
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
await
|
|
24
|
+
```javascript
|
|
25
|
+
await mcp__context7__resolve-library-id({ libraryName: "react" });
|
|
27
26
|
|
|
28
|
-
// Playwright (browser automation)
|
|
29
27
|
await mcp__playwright__browser_navigate({ url: "https://example.com" });
|
|
30
|
-
|
|
31
|
-
// Neon — use `neonctl` CLI + `psql`
|
|
32
|
-
// neonctl connection-string → get DB URL
|
|
33
|
-
// psql <DB_URL> -c "SELECT ..."
|
|
34
28
|
```
|
|
35
29
|
|
|
36
30
|
**Fallback rule:** If an MCP is not available, every phase has a manual alternative using Claude Code native tools (Read, Grep, Glob, Bash).
|
|
@@ -61,36 +55,31 @@ gh issue list --limit 10 --json title,labels
|
|
|
61
55
|
| MCP | Use Case | Example |
|
|
62
56
|
|-----|----------|---------|
|
|
63
57
|
| **Playwright** | Navigate, screenshot, inspect live pages | `mcp__playwright__browser_navigate({ url })` |
|
|
64
|
-
| **Context7** | Component library documentation | `
|
|
58
|
+
| **Context7** | Component library documentation | `mcp__context7__query-docs({ libraryId, query })` |
|
|
59
|
+
|
|
60
|
+
The screenshot is the visual reference; the snapshot is the accessibility tree (headings, buttons, inputs); the resize tests the mobile layout; Context7 brings the component docs for the UI spec.
|
|
65
61
|
|
|
66
62
|
```javascript
|
|
67
|
-
// Screenshot existing app for design reference
|
|
68
63
|
await mcp__playwright__browser_navigate({ url: "https://app.example.com/dashboard" });
|
|
69
64
|
const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
70
|
-
// → Visual reference for UI/UX design
|
|
71
65
|
|
|
72
|
-
// Inspect page structure via accessibility tree
|
|
73
66
|
const snapshot = await mcp__playwright__browser_snapshot();
|
|
74
|
-
// → Structured element tree (headings, buttons, inputs, etc.)
|
|
75
67
|
|
|
76
|
-
// Test responsive layout
|
|
77
68
|
await mcp__playwright__browser_resize({ width: 375, height: 812 });
|
|
78
69
|
const mobileScreenshot = await mcp__playwright__browser_take_screenshot();
|
|
79
70
|
|
|
80
|
-
|
|
81
|
-
const libId = await mcp__context7__resolve_library_id({
|
|
71
|
+
const libId = await mcp__context7__resolve-library-id({
|
|
82
72
|
libraryName: "mudblazor",
|
|
83
73
|
query: "data grid with sorting and filtering"
|
|
84
74
|
});
|
|
85
|
-
const docs = await
|
|
75
|
+
const docs = await mcp__context7__query-docs({
|
|
86
76
|
libraryId: libId,
|
|
87
77
|
query: "DataGrid component props and events"
|
|
88
78
|
});
|
|
89
|
-
|
|
90
|
-
// Fallback: WebSearch for component documentation
|
|
91
|
-
// WebFetch for specific component API pages
|
|
92
79
|
```
|
|
93
80
|
|
|
81
|
+
Fallback: WebSearch for component documentation, WebFetch for specific component API pages.
|
|
82
|
+
|
|
94
83
|
### Gemini Image MCP — AI Image Generation
|
|
95
84
|
|
|
96
85
|
> Available in: **UI/UX** (preview generation), **Implement** (production assets)
|
|
@@ -103,29 +92,27 @@ const docs = await mcp__context7__query_docs({
|
|
|
103
92
|
| `list_images` | Browse image generation history | `mcp__gemini-image-mcp__list_images({ type: "all" })` |
|
|
104
93
|
| `get_image_info` | Get metadata about a specific image | `mcp__gemini-image-mcp__get_image_info({ imageId })` |
|
|
105
94
|
|
|
95
|
+
Read `design-system.md` first to get the project colors and aesthetic direction. Keep edits short and targeted, and iterate with `continue_editing` one change at a time.
|
|
96
|
+
|
|
106
97
|
```javascript
|
|
107
|
-
|
|
108
|
-
// Read design-system.md first to get project colors and aesthetic direction
|
|
109
|
-
const image = await mcp__gemini_image_mcp__generate_image({
|
|
98
|
+
const image = await mcp__gemini-image-mcp__generate_image({
|
|
110
99
|
prompt: "A floating tablet showing a modern dashboard interface on a dark charcoal background with copper and gold accent lighting. Premium luxury aesthetic, photorealistic, cinematic lighting with warm rim light.",
|
|
111
100
|
aspectRatio: "16:9",
|
|
112
101
|
outputPath: "public/images/my-feature/hero-mockup.png"
|
|
113
102
|
});
|
|
114
103
|
|
|
115
|
-
|
|
116
|
-
await mcp__gemini_image_mcp__edit_image({
|
|
104
|
+
await mcp__gemini-image-mcp__edit_image({
|
|
117
105
|
imageSource: "public/images/my-feature/hero-mockup.png",
|
|
118
106
|
prompt: "Make the lighting warmer and add a subtle copper glow around the tablet edges."
|
|
119
107
|
});
|
|
120
108
|
|
|
121
|
-
|
|
122
|
-
await mcp__gemini_image_mcp__continue_editing({
|
|
109
|
+
await mcp__gemini-image-mcp__continue_editing({
|
|
123
110
|
prompt: "More contrast in the shadows."
|
|
124
111
|
});
|
|
125
|
-
|
|
126
|
-
// Fallback: Manual image creation, stock photos, or placeholder divs
|
|
127
112
|
```
|
|
128
113
|
|
|
114
|
+
Fallback: manual image creation, stock photos, or placeholder divs.
|
|
115
|
+
|
|
129
116
|
**Use cases by phase:**
|
|
130
117
|
|
|
131
118
|
| Phase | Use Case | Tool |
|
|
@@ -143,7 +130,7 @@ await mcp__gemini_image_mcp__continue_editing({
|
|
|
143
130
|
| Tool | Use Case | Example |
|
|
144
131
|
|------|----------|---------|
|
|
145
132
|
| **Neon CLI (`neonctl`) / psql** | Database schema, tables, relationships, RLS | `psql <DB_URL> -c "SELECT ..."` |
|
|
146
|
-
| **Context7** | Library docs for architecture decisions | `
|
|
133
|
+
| **Context7** | Library docs for architecture decisions | `mcp__context7__query-docs()` |
|
|
147
134
|
| **GitHub CLI (`gh`)** | Check existing code patterns, PRs | `gh search code` (Bash) |
|
|
148
135
|
|
|
149
136
|
```bash
|
|
@@ -183,26 +170,26 @@ psql $(neonctl connection-string) -c "SELECT tablename, policyname, cmd, qual FR
|
|
|
183
170
|
|
|
184
171
|
| MCP | Use Case | Example |
|
|
185
172
|
|-----|----------|---------|
|
|
186
|
-
| **Context7** | Verify library capabilities, API limits | `
|
|
173
|
+
| **Context7** | Verify library capabilities, API limits | `mcp__context7__query-docs()` |
|
|
187
174
|
| **GitHub CLI (`gh`)** | Check issue discussions, known limitations | `gh search issues` (Bash) |
|
|
188
175
|
|
|
176
|
+
The answer confirms a capability or identifies a limitation for the spec update:
|
|
177
|
+
|
|
189
178
|
```javascript
|
|
190
|
-
|
|
191
|
-
const docs = await mcp__context7__query_docs({
|
|
179
|
+
const docs = await mcp__context7__query-docs({
|
|
192
180
|
libraryId: "/mudblazor/mudblazor",
|
|
193
181
|
query: "DataGrid server-side pagination with virtual scrolling"
|
|
194
182
|
});
|
|
195
|
-
// → Confirms capability or identifies limitation for spec update
|
|
196
|
-
|
|
197
|
-
// Fallback: WebSearch for library capabilities
|
|
198
183
|
```
|
|
199
184
|
|
|
185
|
+
Fallback: WebSearch for library capabilities.
|
|
186
|
+
|
|
200
187
|
### Phase 6 (Tasks) — Planning & Organization
|
|
201
188
|
|
|
202
189
|
| Tool | Use Case | Example |
|
|
203
190
|
|------|----------|---------|
|
|
204
191
|
| **GitHub CLI (`gh`)** | Create issues from tasks, link to milestone | `gh issue create` (Bash) |
|
|
205
|
-
| **Context7** | Estimate complexity based on library docs | `
|
|
192
|
+
| **Context7** | Estimate complexity based on library docs | `mcp__context7__query-docs()` |
|
|
206
193
|
|
|
207
194
|
```bash
|
|
208
195
|
# Create GitHub issues from tasks.json (if team uses GitHub Projects)
|
|
@@ -218,7 +205,7 @@ gh issue create --title "T001: Create Entity Lead" \
|
|
|
218
205
|
|------|----------|---------|
|
|
219
206
|
| **Neon CLI (`neonctl`) / psql** | Run migrations, create RLS policies | `dotnet ef database update` |
|
|
220
207
|
| **GitHub CLI (`gh`)** | Create PR, push branches | `gh pr create` (Bash) |
|
|
221
|
-
| **Context7** | Look up API usage during coding | `
|
|
208
|
+
| **Context7** | Look up API usage during coding | `mcp__context7__query-docs()` |
|
|
222
209
|
| **Playwright** | Smoke test deployed features, verify UI | `mcp__playwright__browser_navigate()` |
|
|
223
210
|
| **Vercel** | Deploy, check logs, manage env vars | `mcp__vercel__list_projects()` |
|
|
224
211
|
| **Azure CLI** | Resource management, deployments | `az` commands |
|
|
@@ -240,35 +227,23 @@ psql $(neonctl connection-string) -c "SELECT column_name, data_type FROM informa
|
|
|
240
227
|
psql $(neonctl connection-string) -c "SELECT policyname, cmd, qual FROM pg_policies WHERE tablename = 'leads'"
|
|
241
228
|
```
|
|
242
229
|
|
|
230
|
+
Look up the API while coding, then smoke test the deployed feature: the snapshot shows whether the page renders, the console messages catch JavaScript errors and failed API calls, and the screenshot goes to `recap.md`. Vercel covers deployment status, logs and environment variables.
|
|
231
|
+
|
|
243
232
|
```javascript
|
|
244
|
-
|
|
245
|
-
const docs = await mcp__context7__query_docs({
|
|
233
|
+
const docs = await mcp__context7__query-docs({
|
|
246
234
|
libraryId: "/dotnet/efcore",
|
|
247
235
|
query: "owned entity types configuration"
|
|
248
236
|
});
|
|
249
237
|
|
|
250
|
-
// Smoke test deployed feature via browser
|
|
251
238
|
await mcp__playwright__browser_navigate({ url: "https://localhost:5001/leads" });
|
|
252
239
|
const snapshot = await mcp__playwright__browser_snapshot();
|
|
253
|
-
// → Verify page renders correctly, check for errors
|
|
254
|
-
|
|
255
|
-
// Check for console errors after deploy
|
|
256
240
|
const logs = await mcp__playwright__browser_console_messages();
|
|
257
|
-
// → Catch JavaScript errors, failed API calls
|
|
258
|
-
|
|
259
|
-
// Screenshot for recap.md documentation
|
|
260
241
|
const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
261
242
|
|
|
262
|
-
// === VERCEL (Deployment & Environment) ===
|
|
263
|
-
|
|
264
|
-
// List projects
|
|
265
243
|
const projects = await mcp__vercel__list_projects();
|
|
266
|
-
|
|
267
|
-
// Check deployment status and logs
|
|
268
244
|
const deployments = await mcp__vercel__get_deployments({ projectId: 'my-project' });
|
|
269
|
-
const
|
|
245
|
+
const deploymentLogs = await mcp__vercel__get_deployment_logs({ deploymentId: deployments[0].uid });
|
|
270
246
|
|
|
271
|
-
// Manage environment variables
|
|
272
247
|
await mcp__vercel__manage_env_vars({
|
|
273
248
|
projectId: 'my-project',
|
|
274
249
|
action: 'set',
|
|
@@ -276,10 +251,10 @@ await mcp__vercel__manage_env_vars({
|
|
|
276
251
|
value: process.env.DATABASE_URL,
|
|
277
252
|
target: ['production', 'preview']
|
|
278
253
|
});
|
|
279
|
-
|
|
280
|
-
// PRs/issues: sempre `gh` CLI (Bash). Deployments fallback: vercel CLI
|
|
281
254
|
```
|
|
282
255
|
|
|
256
|
+
PRs/issues: sempre `gh` CLI (Bash). Deployments fallback: `vercel` CLI.
|
|
257
|
+
|
|
283
258
|
---
|
|
284
259
|
|
|
285
260
|
## MCP vs Native Tools Decision
|
|
@@ -306,76 +281,33 @@ await mcp__vercel__manage_env_vars({
|
|
|
306
281
|
|
|
307
282
|
### Browser (Playwright): Page Automation & Analysis
|
|
308
283
|
|
|
309
|
-
|
|
310
|
-
// === SETUP ===
|
|
311
|
-
// Playwright MCP: npx @playwright/mcp@latest
|
|
312
|
-
// Config in .claude/settings.json or claude_desktop_config.json:
|
|
313
|
-
// { "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] } } }
|
|
284
|
+
Setup: `npx @playwright/mcp@latest`, configured in `.claude/settings.json` or `claude_desktop_config.json` as `{ "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] } } }`.
|
|
314
285
|
|
|
315
|
-
|
|
286
|
+
Prefer the accessibility snapshot to the screenshot: it is structured, LLM-friendly, and returns the element `ref`s that the interaction calls take. `browser_fill_form` replaces the field's existing value; `browser_handle_dialog` answers alert, confirm and prompt.
|
|
316
287
|
|
|
317
|
-
|
|
288
|
+
```javascript
|
|
318
289
|
await mcp__playwright__browser_navigate({ url: "https://app.example.com/dashboard" });
|
|
319
|
-
|
|
320
|
-
// 2. Take accessibility snapshot (preferred — structured, LLM-friendly)
|
|
321
290
|
const snapshot = await mcp__playwright__browser_snapshot();
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// 3. Take visual screenshot (requires --caps vision)
|
|
325
|
-
const screenshot = await mcp__playwright__browser_take_screenshot();
|
|
326
|
-
// → Returns PNG image of current page
|
|
327
|
-
|
|
328
|
-
// === INTERACTION ===
|
|
291
|
+
const screenshot = await mcp__playwright__browser_take_screenshot(); // requires --caps vision
|
|
329
292
|
|
|
330
|
-
// 4. Click an element (use ref from snapshot)
|
|
331
293
|
await mcp__playwright__browser_click({ element: "Submit button", ref: "s1e15" });
|
|
332
|
-
|
|
333
|
-
// 5. Type into an input field
|
|
334
294
|
await mcp__playwright__browser_type({ element: "Search input", ref: "s1e8", text: "query" });
|
|
335
|
-
|
|
336
|
-
// 6. Fill a form field (replaces existing value)
|
|
337
295
|
await mcp__playwright__browser_fill_form({ ref: "s1e8", value: "new value" });
|
|
338
|
-
|
|
339
|
-
// 7. Select dropdown option
|
|
340
296
|
await mcp__playwright__browser_select_option({ element: "Status", ref: "s1e12", values: ["active"] });
|
|
341
|
-
|
|
342
|
-
// 8. Press keyboard key
|
|
343
297
|
await mcp__playwright__browser_press_key({ key: "Enter" });
|
|
344
298
|
|
|
345
|
-
// === TABS & NAVIGATION ===
|
|
346
|
-
|
|
347
|
-
// 9. List open tabs
|
|
348
299
|
const tabs = await mcp__playwright__browser_tabs();
|
|
349
|
-
|
|
350
|
-
// 10. Navigate back
|
|
351
300
|
await mcp__playwright__browser_navigate_back();
|
|
352
|
-
|
|
353
|
-
// 11. Close current tab
|
|
354
301
|
await mcp__playwright__browser_close();
|
|
355
302
|
|
|
356
|
-
// === ADVANCED ===
|
|
357
|
-
|
|
358
|
-
// 12. Evaluate JavaScript in page context
|
|
359
303
|
const result = await mcp__playwright__browser_evaluate({
|
|
360
304
|
expression: "document.querySelectorAll('.error').length"
|
|
361
305
|
});
|
|
362
|
-
|
|
363
|
-
// 13. Get console messages (debug)
|
|
364
306
|
const logs = await mcp__playwright__browser_console_messages();
|
|
365
|
-
|
|
366
|
-
// 14. Get network requests
|
|
367
307
|
const requests = await mcp__playwright__browser_network_requests();
|
|
368
|
-
|
|
369
|
-
// 15. Handle dialog (alert, confirm, prompt)
|
|
370
308
|
await mcp__playwright__browser_handle_dialog({ accept: true });
|
|
371
|
-
|
|
372
|
-
//
|
|
373
|
-
await mcp__playwright__browser_resize({ width: 375, height: 812 }); // iPhone viewport
|
|
374
|
-
|
|
375
|
-
// 17. Save page as PDF (requires --caps pdf)
|
|
376
|
-
await mcp__playwright__browser_pdf_save();
|
|
377
|
-
|
|
378
|
-
// 18. Upload file
|
|
309
|
+
await mcp__playwright__browser_resize({ width: 375, height: 812 });
|
|
310
|
+
await mcp__playwright__browser_pdf_save(); // requires --caps pdf
|
|
379
311
|
await mcp__playwright__browser_file_upload({ ref: "s1e20", paths: ["/path/to/file.png"] });
|
|
380
312
|
```
|
|
381
313
|
|
|
@@ -424,16 +356,15 @@ psql $(neonctl connection-string) -c "SELECT tc.table_name, kcu.column_name, ccu
|
|
|
424
356
|
|
|
425
357
|
### Context7: Library Research
|
|
426
358
|
|
|
359
|
+
For any phase needing library documentation: resolve the library ID first, then query the specific documentation with it.
|
|
360
|
+
|
|
427
361
|
```javascript
|
|
428
|
-
|
|
429
|
-
// Step 1: Resolve library ID
|
|
430
|
-
const lib = await mcp__context7__resolve_library_id({
|
|
362
|
+
const lib = await mcp__context7__resolve-library-id({
|
|
431
363
|
libraryName: "fluent-ui-blazor",
|
|
432
364
|
query: "dialog component with form validation"
|
|
433
365
|
});
|
|
434
366
|
|
|
435
|
-
|
|
436
|
-
const docs = await mcp__context7__query_docs({
|
|
367
|
+
const docs = await mcp__context7__query-docs({
|
|
437
368
|
libraryId: lib.id,
|
|
438
369
|
query: "FluentDialog component usage with EditForm validation"
|
|
439
370
|
});
|