@everystack/mcp 0.2.3 → 0.3.1

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.
Files changed (44) hide show
  1. package/README.md +37 -10
  2. package/dist/adding-database.md +169 -0
  3. package/dist/admin.md +81 -0
  4. package/dist/auth.md +115 -0
  5. package/dist/aws-setup.md +276 -0
  6. package/dist/cli.md +108 -0
  7. package/dist/client-api.md +145 -0
  8. package/dist/core.md +196 -0
  9. package/dist/deployment.md +146 -0
  10. package/dist/events.md +87 -0
  11. package/dist/first-run.md +100 -0
  12. package/dist/getting-started.md +75 -0
  13. package/dist/handler-options.md +114 -0
  14. package/dist/images.md +93 -0
  15. package/dist/index.cjs +23796 -0
  16. package/dist/jobs.md +97 -0
  17. package/dist/logging.md +91 -0
  18. package/dist/plugins.md +68 -0
  19. package/dist/project-claude-md.md +103 -0
  20. package/dist/query-protocol.md +129 -0
  21. package/dist/schema-patterns.md +167 -0
  22. package/dist/security-device.md +99 -0
  23. package/dist/security.md +270 -0
  24. package/dist/ssr.md +82 -0
  25. package/dist/storage.md +63 -0
  26. package/dist/testing.md +118 -0
  27. package/package.json +11 -9
  28. package/src/gates/detectors/embedded-data-bundle.ts +58 -0
  29. package/src/gates/detectors/hand-written-migration.ts +42 -0
  30. package/src/gates/detectors/secret-in-public-env.ts +41 -0
  31. package/src/gates/engine.ts +80 -0
  32. package/src/gates/registry.ts +25 -0
  33. package/src/gates/telemetry.ts +143 -0
  34. package/src/gates/types.ts +70 -0
  35. package/src/governance/cli.ts +193 -0
  36. package/src/governance/grounding.ts +344 -0
  37. package/src/index.ts +97 -50
  38. package/src/prompts/claude-md.ts +92 -0
  39. package/src/prompts/governance-setup.ts +85 -0
  40. package/src/prompts/index.ts +6 -0
  41. package/src/prompts/new-app.ts +4 -1
  42. package/src/prompts/runbook.ts +77 -0
  43. package/src/resources/project-claude-md.md +70 -94
  44. package/src/tools/index.ts +6 -39
@@ -0,0 +1,85 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+
4
+ /**
5
+ * `governance_setup` — guide the agent to wire the everystack governance hooks
6
+ * into the project's `.claude/settings.json`. The server instructions tell the
7
+ * agent that enforcement runs via hooks; this prompt produces the exact block and
8
+ * the confirm-first procedure.
9
+ */
10
+ export function registerGovernanceSetupPrompt(server: McpServer): void {
11
+ server.prompt(
12
+ 'governance_setup',
13
+ 'Install the everystack governance hooks (grounding gate + cheat gates) into .claude/settings.json. The MCP server alone does not enforce — the hooks are the teeth.',
14
+ {
15
+ projectPath: z.string().optional().describe('Absolute path to the project root'),
16
+ mode: z
17
+ .enum(['workspace', 'installed'])
18
+ .optional()
19
+ .describe('"workspace" (run from this monorepo via tsx) or "installed" (the published @everystack/mcp bin). Default: detect.'),
20
+ },
21
+ async ({ projectPath, mode }) => {
22
+ const cmd =
23
+ mode === 'workspace'
24
+ ? 'npx tsx packages/mcp/src/index.ts'
25
+ : mode === 'installed'
26
+ ? 'everystack-mcp'
27
+ : 'everystack-mcp';
28
+ const hooks = {
29
+ hooks: {
30
+ SessionStart: [{ hooks: [{ type: 'command', command: `${cmd} context` }] }],
31
+ PreToolUse: [{ matcher: '*', hooks: [{ type: 'command', command: `${cmd} gate` }] }],
32
+ PostToolUse: [
33
+ { matcher: 'Read', hooks: [{ type: 'command', command: `${cmd} mark` }] },
34
+ { matcher: 'Write|Edit', hooks: [{ type: 'command', command: `${cmd} validate` }] },
35
+ ],
36
+ },
37
+ };
38
+
39
+ return {
40
+ messages: [
41
+ {
42
+ role: 'user' as const,
43
+ content: {
44
+ type: 'text' as const,
45
+ text: [
46
+ `Install the everystack governance hooks for ${projectPath ? 'the project at ' + projectPath : 'this project'}.`,
47
+ '',
48
+ '## Why',
49
+ '',
50
+ 'The everystack MCP server provides knowledge, but ENFORCEMENT (the grounding gate and',
51
+ 'cheat gates) runs only when these Claude Code hooks are installed. Without them, nothing',
52
+ 'catches off-script work (hand-written migrations, data bundled into the app, secrets behind',
53
+ 'EXPO_PUBLIC_*).',
54
+ '',
55
+ '## Procedure (confirm-first — never silent)',
56
+ '',
57
+ '1. Read `docs/governance-setup.md` and `hooks.example.json` from the @everystack/mcp package.',
58
+ `2. Check whether ${projectPath ? projectPath + '/.claude/settings.json' : '.claude/settings.json'} already has these hooks (commands calling \`${cmd} gate\`).`,
59
+ '3. If absent, show the human the exact block below and confirm the command form before writing:',
60
+ ' - **Workspace (this monorepo):** `npx tsx packages/mcp/src/index.ts <sub>`',
61
+ ' - **Installed package:** `everystack-mcp <sub>` (install `@everystack/mcp`; in a hook prefer',
62
+ ' `pnpm exec everystack-mcp <sub>` over bare `npx`, which re-resolves on every tool call).',
63
+ '4. Merge the `hooks` block into `.claude/settings.json` (preserve any existing `hooks`/`permissions`).',
64
+ '5. Remind the human: hooks run a shell command on every tool call. This is a deliberate,',
65
+ ' security-relevant change — it is their call, not yours to make silently.',
66
+ '',
67
+ '## The block to merge',
68
+ '',
69
+ '```json',
70
+ JSON.stringify(hooks, null, 2),
71
+ '```',
72
+ '',
73
+ '## After install',
74
+ '',
75
+ '- A fresh session denies every non-Read tool until the project contract (CLAUDE.md + its',
76
+ ' REQUIRED-READS) is read. That is the grounding gate working.',
77
+ '- `everystack-mcp report` summarizes what the agent tried (the telemetry sensor).',
78
+ ].join('\n'),
79
+ },
80
+ },
81
+ ],
82
+ };
83
+ },
84
+ );
85
+ }
@@ -5,6 +5,9 @@ import { registerDesignSchemaPrompt } from './design-schema.js';
5
5
  import { registerDeployPrompt } from './deploy.js';
6
6
  import { registerDebugPrompt } from './debug.js';
7
7
  import { registerSecurePrompt } from './secure.js';
8
+ import { registerGovernanceSetupPrompt } from './governance-setup.js';
9
+ import { registerClaudeMdPrompt } from './claude-md.js';
10
+ import { registerRunbookPrompt } from './runbook.js';
8
11
 
9
12
  export function registerPrompts(server: McpServer): void {
10
13
  registerNewAppPrompt(server);
@@ -13,4 +16,7 @@ export function registerPrompts(server: McpServer): void {
13
16
  registerDeployPrompt(server);
14
17
  registerDebugPrompt(server);
15
18
  registerSecurePrompt(server);
19
+ registerGovernanceSetupPrompt(server);
20
+ registerClaudeMdPrompt(server);
21
+ registerRunbookPrompt(server);
16
22
  }
@@ -177,7 +177,10 @@ export function registerNewAppPrompt(server: McpServer): void {
177
177
  ].join('\n') : '',
178
178
  '',
179
179
  '## Project Documentation',
180
- 'Read the everystack://project-claude-md resource and write a CLAUDE.md file to the project root.',
180
+ 'Scaffold the project contract (use the claude_md prompt): read the everystack://project-claude-md',
181
+ 'resource and write a CLAUDE.md to the project root. This file activates the grounding gate —',
182
+ 'every later session must read it before editing — so it carries the non-negotiables the cheat',
183
+ 'gates enforce. Keep it current as the project grows (claude_md reconciles it).',
181
184
  `Replace {PROJECT_NAME} with "${effectiveName}", {ONE_LINE_DESCRIPTION} with "${description}",`,
182
185
  'and {ANNOTATED_DIRECTORY_TREE} with the actual project structure created above.',
183
186
  '',
@@ -0,0 +1,77 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+
4
+ /**
5
+ * `runbook` — generate or refresh docs/RUNBOOK.md, the per-app operations manual.
6
+ *
7
+ * The compiler lives in the CLI (`everystack runbook` — db:generate for docs:
8
+ * detection → sections keyed by the app's reality → es:gen/es:slot merge). This
9
+ * prompt is the thin agent layer on top: run the compiler, then fill the EMPTY
10
+ * slots with app-specific narrative the compiler cannot know. Generated blocks
11
+ * belong to the compiler — the agent never writes inside them.
12
+ */
13
+ export function registerRunbookPrompt(server: McpServer): void {
14
+ server.prompt(
15
+ 'runbook',
16
+ 'Generate or refresh docs/RUNBOOK.md — the operations manual compiled from the app\'s detected reality (tier, Models, handlers). Runs `everystack runbook`, then fills empty slots with app narrative (confirm-first). Regenerate after the app grows or an everystack upgrade.',
17
+ {
18
+ projectPath: z.string().optional().describe('Absolute path to the project root (where docs/RUNBOOK.md lives)'),
19
+ },
20
+ async ({ projectPath }) => {
21
+ const root = projectPath ?? '.';
22
+ return {
23
+ messages: [
24
+ {
25
+ role: 'user' as const,
26
+ content: {
27
+ type: 'text' as const,
28
+ text: [
29
+ `Generate or refresh docs/RUNBOOK.md for the project at ${root}.`,
30
+ '',
31
+ '## What this is',
32
+ '',
33
+ 'docs/RUNBOOK.md is the per-app operations manual — how to configure, extend, deploy,',
34
+ 'monitor, and maintain THIS app. It is COMPILED, not authored: `everystack runbook`',
35
+ 'derives it from the app\'s detected reality (installed packages → tier, Models,',
36
+ 'handlers, crons) and regenerating after a change diffs in what\'s new. The division',
37
+ 'of labor: CLAUDE.md is the terse contract (rules); the runbook is procedures.',
38
+ '',
39
+ '## Step 1 — run the compiler',
40
+ '',
41
+ `- If ${root}/docs/RUNBOOK.md exists, run \`everystack runbook --diff\` first and show`,
42
+ ' the human which sections would change.',
43
+ '- Then run `everystack runbook` to write it. If it reports hand-edited generated',
44
+ ' sections (a conflict), do NOT reach for --force — help the human move those words',
45
+ ' into a slot or the Notes section first, then regenerate. --force only with their',
46
+ ' explicit ok.',
47
+ '',
48
+ '## Step 2 — fill the EMPTY slots (and only the slots)',
49
+ '',
50
+ 'The document has two kinds of content, mechanically marked:',
51
+ '- `<!-- es:gen section=... -->` blocks belong to the compiler. NEVER write inside one —',
52
+ ' your words would be overwritten on the next regeneration, and the hash check flags it.',
53
+ '- `<!-- es:slot name=... -->` blocks are the human/agent seam and survive every',
54
+ ' regeneration. A slot still holding its `_(Fill in: ...)_` placeholder is empty.',
55
+ '',
56
+ 'For each empty slot, draft content from what you know of the project:',
57
+ '- `overview` — what the app is, who it serves, what an operator should know first.',
58
+ '- `stage-notes` — deployed URLs per stage, channel-to-stage mapping, release cadence',
59
+ ' (read `.sst/outputs.json` or ask; do not guess URLs).',
60
+ '- `notes` — anything project-specific that fits nowhere else. Leave it if nothing real.',
61
+ '',
62
+ 'Show the human your draft slot content and let them approve before writing — the',
63
+ 'runbook is theirs; you assist, you do not own it.',
64
+ '',
65
+ '## Step 3 — verify',
66
+ '',
67
+ 'Run `everystack runbook --check`: it must exit clean (slot edits never make it stale).',
68
+ 'Tell the human: regenerate any time with `everystack runbook`; CI can enforce currency',
69
+ 'with `--check`; after an everystack upgrade the diff reads as release notes for this app.',
70
+ ].join('\n'),
71
+ },
72
+ },
73
+ ],
74
+ };
75
+ },
76
+ );
77
+ }
@@ -2,13 +2,43 @@
2
2
 
3
3
  > {ONE_LINE_DESCRIPTION}
4
4
 
5
+ This file is the contract for how this project is built. Read it before writing code; it
6
+ overrides convenience. When a rule is hard to follow, that is the signal you are about to
7
+ introduce drift — stop and do it the right way.
8
+
9
+ <!--
10
+ REQUIRED-READS: (optional) a comma-separated list of companion files that must also be
11
+ read before non-Read tools, e.g. `REQUIRED-READS: docs/architecture.md, db/SCHEMA.md`.
12
+ This CLAUDE.md is required automatically — add the line only if real companion docs exist.
13
+ -->
14
+
15
+ ## Non-negotiables
16
+
17
+ These are enforced (everystack cheat gates) and load-bearing. Do not work around them.
18
+
19
+ - **Data lives in PostgreSQL, served through the API — never bundle data into the app.** A
20
+ large `.json`/`.csv` of computed data in the bundle is wrong; model it and serve it, or
21
+ render an empty state if it does not exist yet.
22
+ - **Schema and migrations are generated from Models.** Declare tables with `defineModel`
23
+ (in `models/`), run `everystack db:generate`. Never hand-write a SQL migration, never edit
24
+ the generated `db/schema.ts`. After any change, `db:generate` must be a clean no-op.
25
+ - **Authorization is declared, not hand-written.** Use `can()` abilities on the Model; they
26
+ compile to RLS + grants. Never hand-write `CREATE POLICY`/`GRANT`. RLS is required.
27
+ - **Reuse `@everystack/ui`.** Do not hand-roll a component that already exists there. Style
28
+ with classNames (light + dark), not inline `StyleSheet`.
29
+ - **Secrets stay server-side.** Never put a DB URL, key, or token behind an `EXPO_PUBLIC_*`
30
+ name — those are compiled into the client bundle forever. Use `everystack secrets`.
31
+ - **Apps stay thin.** Screens wire routing and UI; reusable logic lives in packages/`lib`.
32
+
5
33
  ## Start Here
6
34
 
35
+ - `models/` — `defineModel` tables (the source of truth for schema + authz)
7
36
  - `app/` — Expo Router pages (screens, navigation, API routes)
8
37
  - `server/` — Lambda handlers (api.ts, worker.ts, image.ts)
9
- - `db/` — Drizzle schema, migrations, seed data
10
- - `lib/` — Shared code (auth context, API client)
38
+ - `db/` — generated Drizzle schema + migrations (do not edit by hand)
39
+ - `lib/` — shared code (auth context, API client)
11
40
  - `sst.config.ts` — AWS infrastructure definition
41
+ - `docs/RUNBOOK.md` — how to operate this app; regenerate with `everystack runbook`
12
42
 
13
43
  ## Structure
14
44
 
@@ -17,111 +47,57 @@
17
47
  ## Commands
18
48
 
19
49
  ```bash
20
- pnpm install # Install dependencies
21
- pnpm dev # Start Expo dev server
22
- pnpm test # Run all tests
23
- npx drizzle-kit generate # Generate migration from schema changes
24
- npx drizzle-kit migrate # Apply migrations locally
25
- pnpm sst deploy --stage dev # Deploy to AWS
26
- everystack update --channel production # OTA update (no redeploy)
27
- everystack db:migrate # Run migrations on deployed Lambda
28
- everystack db:seed # Seed database (dev only)
29
- everystack db:psql --stage dev # Connect to database
50
+ pnpm install # Install dependencies
51
+ pnpm dev # Start the Expo dev server
52
+ pnpm test # Run all tests (TDD)
53
+ everystack db:generate # Models → next migration (data + authz)
54
+ everystack db:migrate # Apply migrations on the deployed Lambda
55
+ everystack db:seed # Seed the database (dev only)
56
+ everystack deploy --stage dev # Deploy infrastructure (SST)
57
+ everystack update --channel production # OTA update (no redeploy)
58
+ everystack secrets set KEY "value" --stage dev # Set a server-side secret
59
+ everystack bundle:audit <url> # Audit a deployed bundle (weight + leaks)
30
60
  ```
31
61
 
32
62
  ## Key Principles
33
63
 
34
- ### Always TypeScript
35
-
36
- Strict mode, no exceptions. Prefer `unknown` over `any`. Explicit return types on exported functions. Named exports only (no default exports).
37
-
38
- ### Always TDD
39
-
40
- Every feature starts with a failing test.
41
-
42
- 1. Write a failing test that describes expected behavior
43
- 2. Implement the minimum code to make it pass
44
- 3. Refactor while keeping tests green
45
-
46
- Tests live in `__tests__/` mirroring source structure, named `{feature}.test.ts`. Run from package dir: `npx jest __tests__/path/to/test.ts`
47
-
48
- ### Platform Security Over All Else
49
-
50
- Three layers of defense. Each is a complete security boundary:
51
-
52
- 1. **Edge (CloudFront)** — JWT signature + expiry check. Invalid tokens never reach Lambda.
53
- 2. **Handler (Lambda)** — `SET LOCAL ROLE` via pgSettings, RPC role gates, rowOwnership, exposedTables.
54
- 3. **Database (PostgreSQL)** — GRANTs + RLS policies. The single source of truth for authorization.
55
-
56
- Even if the handler has a bug, the database enforces access. Never skip RLS. Never connect as superuser in production.
64
+ ### Models are the source of truth (v3)
57
65
 
58
- ### Progressive Complexity
66
+ Declare tables with `defineModel` (`field`, `can`, relations). A package's full DB slice is a
67
+ `defineModule`; the app composes Modules. `everystack db:generate` compiles them to one
68
+ migration (schema + RLS + grants); `deriveHandlerConfig(models)` derives the API config. You
69
+ never hand-write migrations, RLS, or handler access-control — they are derived, so they cannot
70
+ drift.
59
71
 
60
- Each tier builds on the previous. You never rip out what you have, you add to it.
72
+ ### Security over all else
61
73
 
62
- - **V1:** Static site. Expo + S3 + CloudFront + Lambda SSR + OTA updates. No database.
63
- - **V2:** Add PostgreSQL, PostgREST API, JWT auth, admin dashboard, logging.
64
- - **V3:** Add SQS workers, Sharp image processing, S3 file storage.
74
+ Three layers, each a complete boundary: edge (CloudFront JWT check), handler (pgSettings role +
75
+ ownership), and database (GRANTs + RLS the source of truth). Even if the handler has a bug,
76
+ the database enforces access. Never skip RLS. Never connect as superuser in production.
65
77
 
66
- ### Web Standards
78
+ ### Progressive complexity
67
79
 
68
- Handler uses `Request`/`Response` interface. No Express, no Fastify. Works with Expo Router API routes, Cloudflare Workers, Deno, Bun, or any Web Standard runtime.
80
+ Each tier adds to the previous; you never rip out what you have.
81
+ - **V1:** Static. Expo + S3 + CloudFront + Lambda SSR + OTA. No database.
82
+ - **V2:** Add PostgreSQL, the API, JWT auth, admin, logging.
83
+ - **V3:** Add SQS workers, image processing, S3 file storage.
69
84
 
70
- ### Schema-Agnostic
85
+ ### Test-driven
71
86
 
72
- The library knows nothing about your tables. You pass your Drizzle schema to `createHandler()`. Your schema, your migrations, your database — the library provides the protocol.
73
-
74
- ## The Stack
75
-
76
- **App:**
77
- - TypeScript (strict mode)
78
- - Expo + expo-router (file-based routing)
79
- - React Native + react-native-web (cross-platform)
80
- - @mgcrea/react-native-tailwind (Tailwind CSS styling)
81
-
82
- **Data:**
83
- - drizzle-orm + drizzle-kit (schema, queries, migrations)
84
- - postgres (postgres.js driver)
85
- - PostgreSQL (database)
86
-
87
- **Infrastructure:**
88
- - SST (infrastructure as code)
89
- - AWS (S3, CloudFront, Lambda, RDS Aurora Serverless, SQS)
90
-
91
- **Dev Tooling:**
92
- - jest + ts-jest (testing)
93
- - esbuild (Lambda bundling)
94
- - pnpm (package management)
87
+ Every feature starts with a failing test. Tests in `__tests__/` mirroring source.
95
88
 
96
89
  ## Conventions
97
90
 
98
- ### Naming
99
-
100
- - Package scope: `@everystack/*`
101
- - Files: `kebab-case.ts`
102
- - Exports: named (no default exports)
103
- - Types: PascalCase, no `I` prefix
104
- - Functions: camelCase
105
-
106
- ### Git
107
-
108
- - Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`
109
- - Scope optional: `feat(handler):`, `fix(client):`
110
- - Tests must pass before commit
111
-
112
- ### Code
113
-
114
- - TypeScript strict mode
115
- - Prefer `unknown` over `any`
116
- - Explicit return types on exported functions
117
- - Prefer pure functions over classes
118
- - Web Standard APIs (Request/Response)
119
- - Error handling at boundaries only
91
+ - Package scope `@everystack/*`; files `kebab-case.ts`; named exports (no defaults); types
92
+ PascalCase (no `I` prefix); functions camelCase; DB columns `snake_case`.
93
+ - TypeScript strict; prefer `unknown` over `any`; explicit return types on exports.
94
+ - Conventional commits (`feat:`, `fix:`, `chore:`, `docs:`); tests pass before commit.
120
95
 
121
- ### What NOT to Do
96
+ ## What NOT to Do
122
97
 
123
- - Don't put schema or migrations in the librarythey belong to the app
124
- - Don't couple to Express/Fastifyuse Request/Response
125
- - Don't skip tests to ship faster
126
- - Don't add features beyond what's tested
127
- - Don't require a database for V1the stack works without one
98
+ - Don't bundle large data into the appit lives in the DB, served by the API.
99
+ - Don't hand-write migrations or edit `db/schema.ts`edit the Model, run `db:generate`.
100
+ - Don't hand-write RLS declare `can()` abilities.
101
+ - Don't hand-roll a component that exists in `@everystack/ui`; don't use inline `StyleSheet`.
102
+ - Don't put a secret behind `EXPO_PUBLIC_*`that ships to the client.
103
+ - Don't skip RLS, skip tests, or build features beyond what's tested.
@@ -1,10 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { analyzeProjectStatus } from './project-status.js';
4
- import { analyzeSchema } from './schema-analyze.js';
5
- import { validateProject } from './project-validate.js';
6
3
  import { checkEnvironment } from './check-environment.js';
7
4
 
5
+ // NOTE (Brick 0): the legacy regex tools — project_status, schema_analyze,
6
+ // project_validate — are intentionally NOT registered. They predate the v3
7
+ // Model/Module surface and return wrong answers on Model-based apps (e.g. they
8
+ // report a securely-derived handler config as "all tables accessible"). They are
9
+ // rebuilt on @everystack/model in Brick 1 and re-registered there. Their source
10
+ // files remain for that rewrite to reference.
8
11
  export function registerTools(server: McpServer): void {
9
12
  server.tool(
10
13
  'check_environment',
@@ -19,40 +22,4 @@ export function registerTools(server: McpServer): void {
19
22
  };
20
23
  },
21
24
  );
22
-
23
- server.tool(
24
- 'project_status',
25
- 'Detect the everystack tier (V1/V2/V3), installed packages, project structure, and deployment state. Run this first to understand the user\'s project.',
26
- { projectPath: z.string().describe('Absolute path to the project root (directory containing package.json)') },
27
- async ({ projectPath }) => {
28
- const status = analyzeProjectStatus(projectPath);
29
- return {
30
- content: [{ type: 'text' as const, text: JSON.stringify(status, null, 2) }],
31
- };
32
- },
33
- );
34
-
35
- server.tool(
36
- 'schema_analyze',
37
- 'Parse Drizzle schema files and cross-reference with handler configuration. Shows tables, columns, relations, and detects misconfigurations.',
38
- { projectPath: z.string().describe('Absolute path to the project root (directory containing package.json)') },
39
- async ({ projectPath }) => {
40
- const analysis = analyzeSchema(projectPath);
41
- return {
42
- content: [{ type: 'text' as const, text: JSON.stringify(analysis, null, 2) }],
43
- };
44
- },
45
- );
46
-
47
- server.tool(
48
- 'project_validate',
49
- 'Check for common mistakes, security gaps, and convention violations. Returns errors (must fix), warnings (should fix), and info (nice to fix).',
50
- { projectPath: z.string().describe('Absolute path to the project root (directory containing package.json)') },
51
- async ({ projectPath }) => {
52
- const report = validateProject(projectPath);
53
- return {
54
- content: [{ type: 'text' as const, text: JSON.stringify(report, null, 2) }],
55
- };
56
- },
57
- );
58
25
  }