@donotdev/mcp-server 0.0.1 → 0.0.3

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 CHANGED
@@ -1,102 +1,143 @@
1
1
  # DoNotDev MCP Server
2
2
 
3
- MCP server that provides component type lookups from published `.d.ts` files. Prevents AI hallucination by giving access to actual TypeScript definitions.
3
+ Intelligence Engine for the DoNotDev framework. Provides type intelligence, convention enforcement, WAI-WAY phase tracking, and project-level memory with a closed lessons loop.
4
4
 
5
5
  ## Setup
6
6
 
7
- ### 1. Build
7
+ ### For Consumers (via npx/bunx)
8
8
 
9
- ```bash
10
- cd packages/mcp-server
11
- bun install
12
- bun run build
13
- ```
14
-
15
- ### 2. Configure Claude Code
9
+ Pre-configured by `dndev init`. To re-configure: `dndev agent`.
16
10
 
17
- Add to your project's `.mcp.json`:
11
+ Manual setup add to `.mcp.json`:
18
12
 
19
13
  ```json
20
14
  {
21
15
  "mcpServers": {
22
16
  "donotdev": {
23
- "command": "node",
24
- "args": ["/absolute/path/to/dndev/packages/mcp-server/dist/index.js"]
17
+ "command": "bunx",
18
+ "args": ["@donotdev/mcp-server"]
25
19
  }
26
20
  }
27
21
  }
28
22
  ```
29
23
 
30
- Or for Claude Desktop, add to `~/.claude/claude_desktop_config.json`:
24
+ ### For Framework Development
31
25
 
32
26
  ```json
33
27
  {
34
28
  "mcpServers": {
35
29
  "donotdev": {
36
30
  "command": "node",
37
- "args": ["/absolute/path/to/dndev/packages/mcp-server/dist/index.js"]
31
+ "args": ["/path/to/dndev/packages/mcp-server/dist/index.js"]
38
32
  }
39
33
  }
40
34
  }
41
35
  ```
42
36
 
43
- ### 3. Restart Claude Code
37
+ Build: `cd packages/mcp-server && bun run build`
38
+
39
+ ## Tools (9)
40
+
41
+ ### Phase Tracking
42
+
43
+ #### `start_phase`
44
+ Begin a WAI-WAY phase. Returns blueprint, agent definition, context, and lessons from previous sessions.
44
45
 
45
- ```bash
46
- claude --mcp-debug
46
+ ```
47
+ Input: { "phase": 0 }
48
+ Input: { "phase": 3, "module": "billing" } ← scoped to module (large projects)
49
+ Output: Blueprint + agent persona + context + lessons + enforcement rules
47
50
  ```
48
51
 
49
- ## Tools
52
+ #### `complete_phase`
53
+ Validate files and submit phase for user review. Convention checks + symbol verification run automatically when files are provided. Does NOT auto-advance.
50
54
 
51
- ### `lookup_component`
55
+ ```
56
+ Input: { "files": ["src/pages/UsersPage.tsx"], "lesson": "Access rules must match spec" }
57
+ Output: Validation result. If clean → pending review. If issues → fix list.
58
+ ```
52
59
 
53
- Get TypeScript props/types for a component.
60
+ #### `approve_phase`
61
+ User confirmed the phase output. Marks phase as completed.
54
62
 
55
63
  ```
56
- Input: { "component": "Button" }
57
- Output: Full ButtonProps interface from .d.ts
64
+ Input: {}
65
+ Output: Phase approved. Next: call start_phase(2) to begin ENTITIES.
58
66
  ```
59
67
 
68
+ #### `get_phase_status`
69
+ Current phase, symbols tracked, review status, completed phases.
70
+
60
71
  ```
61
- Input: { "component": "EntityFormRenderer", "package": "@donotdev/ui" }
62
- Output: Full EntityFormRendererProps interface
72
+ Input: {}
73
+ Output: Active: Phase 1 (SCAFFOLD), Symbols: 3, Pending review: no, Completed: 0: BRAINSTORM
63
74
  ```
64
75
 
65
- ### `list_components`
76
+ ### Knowledge
66
77
 
67
- List all exports from a package.
78
+ #### `lookup_symbol`
79
+ Get TypeScript types for any DoNotDev symbol. Reads actual `.d.ts` files. **Tracked per phase** — `complete_phase` flags components used without lookup.
68
80
 
69
81
  ```
70
- Input: { "package": "@donotdev/components" }
71
- Output: Button, Text, Stack, Card, Grid, Section, Badge, Spinner, ...
82
+ Input: { "symbol": "DataTable" }
83
+ Output: Full DataTableProps interface with JSDoc from @donotdev/crud
72
84
  ```
73
85
 
74
- ### `list_packages`
86
+ #### `get_guide`
87
+ Fetch framework setup guides from `guides/dndev/`.
75
88
 
76
- List all available DoNotDev packages.
89
+ ```
90
+ Input: { "topic": "CRUD" }
91
+ Output: CRUD setup guide content
92
+ ```
93
+
94
+ #### `get_guideline`
95
+ Fetch architecture guidelines. Supports `topic:section` for deep dives.
77
96
 
78
97
  ```
79
- Input: {}
80
- Output: @donotdev/components, @donotdev/ui, @donotdev/core, ...
98
+ Input: { "topic": "styling:colors" }
99
+ Output: Colors section of styling guideline
81
100
  ```
82
101
 
83
- ## How It Works
102
+ #### `search_framework`
103
+ Search across all guides and exported symbols.
84
104
 
85
- 1. AI calls `lookup_component({ component: "Button" })`
86
- 2. Server reads `node_modules/@donotdev/components/dist/index.d.ts`
87
- 3. Extracts `ButtonProps` interface using regex
88
- 4. Returns actual TypeScript definition
89
- 5. AI sees real props, can't hallucinate
105
+ ```
106
+ Input: { "query": "EntityList" }
107
+ Output: [SYMBOL] @donotdev/crud: EntityList, EntityListProps
108
+ [GUIDE] CRUD_SETUP.md
109
+ ```
90
110
 
91
- ## Usage in CLAUDE.md
111
+ ### Memory
92
112
 
93
- Add to your project's CLAUDE.md:
113
+ #### `record_lesson`
114
+ Append a lesson to `.dndev/LESSONS.md`. Auto-tags with current phase. **Lessons are returned on the next `start_phase` call** — closing the loop.
94
115
 
95
- ```markdown
96
- ## Component Usage
116
+ ```
117
+ Input: { "lesson": "Firebase emulator must be running for auth tests" }
118
+ Output: Lesson recorded. It will be included in the next start_phase() call.
119
+ ```
97
120
 
98
- BEFORE writing any component, call:
99
- - `lookup_component` to get the actual props
121
+ ## How It Works
100
122
 
101
- NEVER guess props. If MCP unavailable, ask user.
102
123
  ```
124
+ start_phase(N) → blueprint + context + lessons
125
+
126
+ work → follow blueprint, lookup_symbol for every component
127
+
128
+ complete_phase(files) → convention check + symbol verification + submit for review
129
+
130
+ [user reviews]
131
+
132
+ approve_phase() → phase done, advance
133
+ ```
134
+
135
+ No MCP-level file gating. The IDE handles file write approval natively.
136
+
137
+ ## Security
138
+
139
+ - **Path traversal protection:** File paths in `complete_phase` are resolved and verified against project root
140
+ - **Project root discovery:** Walks up from cwd looking for `AI.md`, `.dndev/`, or `package.json`
141
+ - **Lessons capped:** Only the last 50 lines of LESSONS.md are returned to prevent context bloat
142
+ - **Protocol versioning:** Auto-resets on version mismatch (prevents stale state)
143
+ - **Bounded search:** `findNodeModules` limited to 10 directory levels
package/dist/index.d.ts CHANGED
@@ -1,7 +1,3 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * @fileoverview DoNotDev MCP Server
4
- * @description Provides component type lookups from published .d.ts files
5
- */
6
2
  export {};
7
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -1,10 +1,67 @@
1
1
  #!/usr/bin/env node
2
- import{Server as v}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as y}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as k,ListToolsRequestSchema as S}from"@modelcontextprotocol/sdk/types.js";import{readFileSync as f,existsSync as m}from"fs";import{join as a}from"path";const g=["@donotdev/components","@donotdev/adv-comps","@donotdev/core","@donotdev/crud","@donotdev/firebase","@donotdev/auth","@donotdev/billing","@donotdev/oauth","@donotdev/ui","@donotdev/templates","@donotdev/functions","@donotdev/providers"];function w(){let e=process.cwd();for(;e!=="/";){const o=a(e,"node_modules");if(m(o))return o;e=a(e,"..")}return null}function $(e,o){const c=new RegExp(`export\\s+interface\\s+${o}\\s*(?:<[^>]+>)?\\s*(?:extends[^{]+)?\\{[^}]+\\}`,"gs");let n=e.match(c);if(n)return n[0];const s=new RegExp(`export\\s+type\\s+${o}\\s*(?:<[^>]+>)?\\s*=[^;]+;`,"gs");if(n=e.match(s),n)return n[0];const r=new RegExp(`export\\s+const\\s+${o}\\s*[=:][^;]+;`,"gs");return n=e.match(r),n?n[0]:null}function R(e){const o=[],c=e.matchAll(/export\s*\{([^}]+)\}/g);for(const t of c)if(t[1]){const d=t[1].split(",").map(p=>p.trim().split(" ")[0]).filter(p=>!!p);o.push(...d)}const n=e.matchAll(/export\s+interface\s+(\w+)/g);for(const t of n)t[1]&&o.push(t[1]);const s=e.matchAll(/export\s+type\s+(\w+)/g);for(const t of s)t[1]&&o.push(t[1]);const r=e.matchAll(/export\s+const\s+(\w+)/g);for(const t of r)t[1]&&o.push(t[1]);const i=e.matchAll(/export\s+default\s+(\w+)/g);for(const t of i)t[1]&&o.push(t[1]);return[...new Set(o)].filter(t=>t&&t!=="default")}const l=new v({name:"donotdev-mcp",version:"0.0.1"},{capabilities:{tools:{}}});l.setRequestHandler(S,async()=>({tools:[{name:"lookup_component",description:"Get TypeScript props/types for a DoNotDev component. Returns the actual interface from .d.ts files.",inputSchema:{type:"object",properties:{component:{type:"string",description:'Component name (e.g., "Button", "ButtonProps", "Stack", "EntityFormRenderer")'},package:{type:"string",description:'Package name (e.g., "@donotdev/components"). If omitted, searches all packages.'}},required:["component"]}},{name:"list_components",description:"List all exported components/types from a DoNotDev package",inputSchema:{type:"object",properties:{package:{type:"string",description:'Package name (e.g., "@donotdev/components")'}},required:["package"]}},{name:"list_packages",description:"List all DoNotDev packages",inputSchema:{type:"object",properties:{}}}]})),l.setRequestHandler(k,async e=>{const{name:o,arguments:c}=e.params,n=w();if(!n)return{content:[{type:"text",text:"Error: node_modules not found. Run from project directory."}]};if(o==="list_packages")return{content:[{type:"text",text:`Available packages:
3
- ${g.filter(r=>m(a(n,r,"dist","index.d.ts"))).join(`
4
- `)}`}]};if(o==="list_components"){const s=c?.package,r=a(n,s,"dist","index.d.ts");if(!m(r))return{content:[{type:"text",text:`Error: ${s} not found at ${r}`}]};const i=f(r,"utf-8"),t=R(i);return{content:[{type:"text",text:`Exports from ${s}:
5
- ${t.join(`
6
- `)}`}]}}if(o==="lookup_component"){const s=c?.component,r=c?.package,i=[s,`${s}Props`,`${s.replace(/Props$/,"")}Props`],t=r?[r]:g;for(const d of t){const p=a(n,d,"dist","index.d.ts");if(!m(p))continue;const x=f(p,"utf-8");for(const h of i){const u=$(x,h);if(u)return{content:[{type:"text",text:`Found in ${d}:
2
+ import{Server as oe}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as se}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as ie,ListToolsRequestSchema as re}from"@modelcontextprotocol/sdk/types.js";import{readFileSync as $,existsSync as m,readdirSync as T,statSync as ae,writeFileSync as I,mkdirSync as D}from"fs";import{join as h,resolve as W}from"path";const G=2,Z=10;function ce(){let r=process.cwd();for(let s=0;s<Z;s++){if(m(h(r,"AI.md"))||m(h(r,".dndev"))||m(h(r,"package.json")))return r;const c=W(r,"..");if(c===r)break;r=c}return process.cwd()}const w=ce(),x=h(w,".dndev"),q=h(x,"protocol.json"),L=h(x,"LESSONS.md"),j=h(x,"implementation.md"),B=h(x,"captain-log.json"),le=h(w,"docs","architecture"),H=h(w,"packages","cli","templates","root-consumer","guides","dndev"),C=[{id:0,name:"BRAINSTORM",agent:"extractor",blueprint:"0_brainstorm",agentFile:"extractor"},{id:1,name:"SCAFFOLD",agent:"extractor",blueprint:"1_scaffold",agentFile:"extractor"},{id:2,name:"ENTITIES",agent:"architect",blueprint:"2_entities",agentFile:"architect"},{id:3,name:"COMPOSE",agent:"builder",blueprint:"3_compose",agentFile:"builder"},{id:4,name:"CONFIGURE",agent:"polisher",blueprint:"4_configure",agentFile:"polisher"}],F={version:G,currentPhase:null,phaseName:null,agent:null,startedAt:null,completedPhases:[],lookedUpSymbols:[],pendingReview:!1,currentModule:null};function k(){if(!m(q))return{...F,completedPhases:[],lookedUpSymbols:[]};try{const r=JSON.parse($(q,"utf-8"));return r.version!==G?{...F,completedPhases:[],lookedUpSymbols:[]}:{...F,...r}}catch{return{...F,completedPhases:[],lookedUpSymbols:[]}}}function U(r){m(x)||D(x,{recursive:!0}),I(q,JSON.stringify(r,null,2))}function pe(r){const s=W(w,r);return s.startsWith(w)?s:null}function ue(r){if(!m(L))return"";const s=$(L,"utf-8");if(!r)return s;const c=s.split(`
3
+ `),l=[];for(const n of c){if(!n.startsWith("- [")){l.push(n);continue}const o=n.match(/\[Phase (\d+):/),t=o?parseInt(o[1]):null,a=[...n.matchAll(/\[([^\]]*)\]/g)].map(u=>u[1]).filter(u=>!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(u)&&!/^Phase \d+:/.test(u)).flatMap(u=>u.split(",").map(g=>g.trim().toLowerCase())).filter(Boolean),p=a.length===0,f=r.phase!==void 0&&t!==null&&(t===r.phase||t===r.phase-1),d=!!r.module&&a.includes(r.module.toLowerCase());(p||f||d)&&l.push(n)}const e=l.join(`
4
+ `).trim();return!e||e==="# Lessons Learned"?"":e}function Q(r,s="",c){const l=c&&c.length>0?`[${c.join(", ")}]`:"",e=`
5
+ - [${new Date().toLocaleDateString()}]${s}${l} ${r}`;m(x)||D(x,{recursive:!0}),m(L)?I(L,$(L,"utf-8")+e):I(L,`# Lessons Learned
6
+ `+e)}function O(){return m(j)?$(j,"utf-8"):null}function K(r){m(x)||D(x,{recursive:!0}),I(j,r)}function E(r,s){const c=r.split(`
7
+ `),l=[];let e=null,n=0,o=0;for(const i of c)/^###?\s/.test(i)&&!i.startsWith("# Implementation")&&(e&&l.push(e),e={title:i.replace(/^#+\s*/,""),total:0,done:0}),/^\s*- \[x\]/i.test(i)?(n++,o++,e&&e.done++,e&&e.total++):/^\s*- \[ \]/.test(i)&&(o++,e&&e.total++);e&&l.push(e);let t=r;if(s){const i=s.toLowerCase(),a=[];let p=!1;for(const f of c)/^###?\s/.test(f)&&(p=f.toLowerCase().includes(i)),p&&a.push(f);t=a.length>0?a.join(`
8
+ `):r}return{stats:{total:o,done:n,pending:o-n,percent:o>0?Math.round(n/o*100):0},sections:l.filter(i=>i.total>0),filtered:t}}function de(r){const s=["# Implementation Progress","",`**Last updated:** ${new Date().toISOString()}`,"**Current phase:** 0 - BRAINSTORM",""];for(const c of r){s.push(`### ${c.title}`);for(const l of c.items)s.push(`- [ ] ${l}`);s.push("")}return s.push("## Notes",""),s.join(`
9
+ `)}function me(){const r=V();if(!r)return null;const s=Y(r,"spec_template");if(!s)return null;const c=[{title:"Phase 0: BRAINSTORM",items:["Spec complete","User validated"]}],l=s.matchAll(/### Entity:\s*\[?(\w+)\]?/g),e=[];for(const t of l)t[1]&&t[1]!=="Name"&&e.push(t[1]);const n=s.matchAll(/\|\s*`?\/[\w-]*`?\s*\|\s*(\w+Page)\s*\|/g),o=[];for(const t of n)t[1]&&o.push(t[1]);return c.push({title:"Phase 1: SCAFFOLD",items:o.length>0?o.map(t=>`${t}.tsx`):["(generate from validated spec)"]}),c.push({title:"Phase 2: ENTITIES",items:e.length>0?e.map(t=>`${t} entity`):["(generate from validated spec)"]}),c.push({title:"Phase 3: COMPOSE",items:o.length>0?o.map(t=>`${t} composition`):["(generate from validated spec)"]}),c.push({title:"Phase 4: CONFIGURE",items:["Tests generated","Firestore rules","Config finalized","Mobile check"]}),c}function fe(r,s){const c=O();if(!c)return;const l=c.replace(/\*\*Current phase:\*\*\s*.*/,`**Current phase:** ${r} - ${s}`);l!==c&&K(l.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`))}function M(){if(!m(B))return{project:w.split("/").pop()||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]};try{return JSON.parse($(B,"utf-8"))}catch{return{project:w.split("/").pop()||"unknown",started:new Date().toISOString().split("T")[0],sessions:[]}}}function he(r){const s=M();s.sessions.push(r),m(x)||D(x,{recursive:!0}),I(B,JSON.stringify(s,null,2))}const J=["@donotdev/core","@donotdev/ui","@donotdev/components","@donotdev/templates","@donotdev/crud","@donotdev/auth","@donotdev/billing","@donotdev/oauth","@donotdev/adv-comps","@donotdev/firebase","@donotdev/functions","@donotdev/mcp-server"],ge=["@donotdev/cli"],ye=[...J,...ge];function $e(){let r=w;for(let s=0;s<Z;s++){const c=h(r,"node_modules");if(m(c))return c;const l=W(r,"..");if(l===r)break;r=l}return null}function ee(){const r=h(w,"guides","dndev");return m(r)?r:m(H)?H:null}function V(){const r=h(w,"guides","wai-way");if(m(r))return r;const s=h(w,"packages","cli","templates","root-consumer","guides","wai-way");return m(s)?s:null}function Y(r,s){for(const c of[".md",".md.example"]){const l=h(r,s+c);if(m(l))return $(l,"utf-8")}return null}function z(r){const s=[];if(!m(r))return s;const c=T(r);for(const l of c){const e=h(r,l);try{ae(e).isDirectory()?s.push(...z(e)):l.endsWith(".d.ts")&&s.push(e)}catch{}}return s}function Se(r,s){const c=new RegExp(`(\\/\\*\\*[\\s\\S]*?\\*\\/\\s*)?(?:export\\s+)?(?:interface|type|const|function|class)\\s+${s}\\b[\\s\\S]*?(?:;|\\{(?:[^{}]*|\\{[^{}]*\\})*\\})`,"g"),l=r.match(c);return l?l[0].trim():null}function xe(r){const s=[],c=r.matchAll(/export\s*\{([^}]+)\}/g);for(const t of c)if(t[1]){const i=t[1].split(",").map(a=>a.trim().split(" ")[0]).filter(a=>!!a);s.push(...i)}const l=r.matchAll(/export\s+interface\s+(\w+)/g);for(const t of l)t[1]&&s.push(t[1]);const e=r.matchAll(/export\s+type\s+(\w+)/g);for(const t of e)t[1]&&s.push(t[1]);const n=r.matchAll(/export\s+const\s+(\w+)/g);for(const t of n)t[1]&&s.push(t[1]);const o=r.matchAll(/export\s+default\s+(\w+)/g);for(const t of o)t[1]&&s.push(t[1]);return[...new Set(s)].filter(t=>t&&t!=="default")}function ve(r){if(!m(r))return null;try{const s=$(r,"utf-8").split(`
10
+ `);let c=!1;for(const l of s){if(l.startsWith("# ")){c=!0;continue}if(c&&l.trim().length>0)return l.trim()}}catch{}return null}function te(r,s){const c=m(h(r,"context_map.json"))?h(r,"context_map.json"):h(r,"context_map.json.example");if(!m(c))return null;try{const l=JSON.parse($(c,"utf-8")),n=Object.keys(l.phases||{}).find(o=>o.startsWith(`${s}_`));if(n&&l.phases[n])return l.phases[n]}catch{}return null}function we(r){const s=[];if(!m(r))return[`File not found: ${r}`];const l=$(r,"utf-8").split(`
11
+ `),e=r.split("/").pop()||"";if(!e.endsWith(".tsx")&&!e.endsWith(".ts"))return[];for(let o=0;o<l.length;o++){const t=l[o],i=o+1;/style\s*=\s*\{\{/.test(t)&&s.push(`L${i}: Inline style found. Use className or framework utilities.`),/fontSize|font-size/i.test(t)&&!/\/\/|\/\*|\*/.test(t.trimStart().substring(0,2))&&s.push(`L${i}: fontSize detected. Use Text level or Card title/subtitle props.`),/text-align:\s*(left|right)|textAlign:\s*['"]?(left|right)/i.test(t)&&s.push(`L${i}: textAlign left/right detected. Use start/end for RTL support.`),/\brequire\s*\(/.test(t)&&!/\/\/|\/\*|\*/.test(t.trimStart().substring(0,2))&&s.push(`L${i}: require() found. Use ESM import.`),/\.toLocaleDateString|\.toLocaleTimeString|new Intl\.DateTimeFormat/i.test(t)&&s.push(`L${i}: Manual date formatting. Use formatDate() from @donotdev/core.`)}const n=l.map((o,t)=>({line:o,num:t+1})).filter(({line:o})=>/^import\s/.test(o));if(n.length>1){let o=0;for(const{line:t,num:i}of n){let a=4;if(/from\s+['"]react/.test(t)?a=1:/from\s+['"]@donotdev\//.test(t)?a=3:/from\s+['"][^.]/.test(t)&&(a=2),a<o){s.push(`L${i}: Import order violation. Expected: React > vendors > @donotdev > relative.`);break}o=a}}return s}function be(r,s){const c=[],l=/<([A-Z]\w+)/g;for(const e of r){if(!m(e))continue;const n=$(e,"utf-8");if(!/@donotdev\//.test(n))continue;const o=n.matchAll(l);for(const t of o){const i=t[1];i&&(["React","Fragment","Suspense","Provider","Router","Route","Switch"].includes(i)||!s.includes(i)&&!s.includes(`${i}Props`)&&c.push(`${e}: <${i}> used but lookup_symbol("${i}") was never called. Props may be hallucinated.`))}}return[...new Set(c)]}const X=new oe({name:"donotdev-mcp",version:"0.0.3"},{capabilities:{tools:{}}});X.setRequestHandler(re,async()=>({tools:[{name:"start_phase",description:"Begin a WAI-WAY phase (0-4). Returns blueprint, agent definition, context, and lessons. For large projects, pass module to scope work.",inputSchema:{type:"object",properties:{phase:{type:"number",description:"Phase number: 0=BRAINSTORM, 1=SCAFFOLD, 2=ENTITIES, 3=COMPOSE, 4=CONFIGURE"},module:{type:"string",description:'Optional module name to scope work (e.g., "user-management", "billing"). For large projects.'}},required:["phase"]}},{name:"complete_phase",description:"Validate and submit phase for user review. Pass files to run convention checks and symbol verification. Phase stays pending until user calls approve_phase.",inputSchema:{type:"object",properties:{files:{type:"array",items:{type:"string"},description:"File paths to validate (relative to project root). Convention checks + symbol usage verification run automatically."},lesson:{type:"string",description:"Optional lesson learned during this phase"},summary:{type:"string",description:`Brief outcome summary for the captain's log (e.g., "8 pages scaffolded, auth flow complete")`}}}},{name:"approve_phase",description:"User approves the completed phase after review. Only call this after complete_phase and user has confirmed the output is correct.",inputSchema:{type:"object",properties:{}}},{name:"get_phase_status",description:"Get current WAI-WAY phase, completed phases, active agent, lookup coverage, and pending review status.",inputSchema:{type:"object",properties:{}}},{name:"lookup_symbol",description:"Get JSDoc context, @examples, and TypeScript types for any DoNotDev symbol. MUST be called before using any @donotdev component.",inputSchema:{type:"object",properties:{symbol:{type:"string",description:'Symbol name (e.g., "defineEntity", "useCrud", "DataTable")'},package:{type:"string",description:"Package name (optional)"}},required:["symbol"]}},{name:"get_guide",description:"Fetch a framework setup guide from guides/dndev/ (e.g., CRUD setup, Auth setup).",inputSchema:{type:"object",properties:{topic:{type:"string",description:'Topic (e.g., "CRUD", "AUTH")'}},required:["topic"]}},{name:"get_guideline",description:"Fetch architectural guidelines for maintenance or app building.",inputSchema:{type:"object",properties:{topic:{type:"string",description:'Topic with optional section (e.g., "styling", "styling:colors")'}},required:["topic"]}},{name:"search_framework",description:"Search across ALL guides and public symbols for a keyword.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Keyword or phrase"}},required:["query"]}},{name:"list_features",description:"List all framework packages with a one-line summary from each README. Call in Phase 0 before designing custom solutions.",inputSchema:{type:"object",properties:{}}},{name:"init_implementation",description:"Create .dndev/implementation.md to track progress across sessions. Pass sections manually or use from_spec to auto-generate from spec_template.md.",inputSchema:{type:"object",properties:{sections:{type:"array",items:{type:"object",properties:{title:{type:"string",description:'Section heading (e.g., "Phase 1: SCAFFOLD", "Auth Module")'},items:{type:"array",items:{type:"string"},description:"Checklist items for this section"}},required:["title","items"]},description:"Manual sections with checklist items"},from_spec:{type:"boolean",description:"Auto-generate sections from spec_template.md (entities \u2192 Phase 2, pages \u2192 Phase 1/3)"},force:{type:"boolean",description:"Overwrite existing implementation.md without warning"}}}},{name:"update_progress",description:"Tick or untick an item in .dndev/implementation.md. Matches by substring. Optionally add a note.",inputSchema:{type:"object",properties:{item:{type:"string",description:'Substring to match the checklist item (e.g., "HomePage", "Product entity")'},done:{type:"boolean",description:"true = tick [x], false = untick [ ]"},note:{type:"string",description:"Optional note appended to the Notes section"}},required:["item","done"]}},{name:"get_progress",description:"Read .dndev/implementation.md with progress stats. Optionally filter to a section.",inputSchema:{type:"object",properties:{section:{type:"string",description:'Filter to section containing this text (e.g., "Phase 1", "SCAFFOLD")'}}}},{name:"get_project_history",description:"Get the captain's log: full session history with metrics. For post-mortem, team review, and project analysis.",inputSchema:{type:"object",properties:{}}},{name:"record_lesson",description:"Record a lesson/gotcha to .dndev/LESSONS.md with inline tags. Filtered by phase + module on next start_phase. Also add a code comment where the gotcha applies.",inputSchema:{type:"object",properties:{lesson:{type:"string",description:"The lesson content (gotcha, quirk, operational knowledge)"},tags:{type:"array",items:{type:"string"},description:'Tags for filtering: entity names, service names, module names (e.g., ["stripe", "billing", "webhook"])'}},required:["lesson"]}}]})),X.setRequestHandler(ie,async r=>{const{name:s,arguments:c}=r.params,l=$e();if(s==="start_phase"){const e=c?.phase,n=c?.module,o=C.find(S=>S.id===e);if(!o)return{content:[{type:"text",text:`Invalid phase: ${e}. Valid: 0-4.`}],isError:!0};const t=V();if(!t)return{content:[{type:"text",text:"Error: guides/wai-way/ directory not found."}],isError:!0};const i=Y(h(t,"blueprints"),o.blueprint),a=Y(h(t,"agents"),o.agentFile),p=te(t,e),f=ue({phase:e,module:n}),d=k();d.version=G,d.currentPhase=o.id,d.phaseName=o.name,d.agent=o.agent,d.startedAt=new Date().toISOString(),d.lookedUpSymbols=[],d.pendingReview=!1,d.currentModule=n||null,U(d);const u=[`# Phase ${o.id}: ${o.name}`,`**Agent:** ${o.agent}`];n&&u.push(`**Module:** ${n}`);const g=d.completedPhases.sort().map(S=>{const N=C.find(y=>y.id===S);return N?N.name:`${S}`}),v=O(),P=v?E(v):null,R=M(),_=[`
12
+ ## Project State`];if(_.push(`- **Phase:** ${o.id}/4 ${o.name}${g.length>0?` (done: ${g.join(", ")})`:""}`),P&&_.push(`- **Progress:** ${P.stats.done}/${P.stats.total} (${P.stats.percent}%)`),R.sessions.length>0&&_.push(`- **Sessions so far:** ${R.sessions.length}`),u.push(..._),p&&u.push(`
13
+ ## Phase Context`,`**Goal:** ${p.goal}`,`**Done when:** ${p.done_when}`,`**Output:** ${p.output}`,"**Read these files first:**",...p.read_files.map(S=>`- ${S}`)),i&&u.push(`
14
+ ---
15
+ ## Blueprint
7
16
 
8
- ${u}`}]}}}return{content:[{type:"text",text:`Not found: ${s}
17
+ ${i}`),a&&u.push(`
18
+ ---
19
+ ## Agent Definition
9
20
 
10
- Tip: Try lookup_component with just the component name (e.g., "Button" not "ButtonProps"), or use list_components to see available exports.`}]}}return{content:[{type:"text",text:`Unknown tool: ${o}`}]}});async function b(){const e=new y;await l.connect(e)}b().catch(console.error);
21
+ ${a}`),f.trim().length>20&&u.push(`
22
+ ---
23
+ ## Lessons (Filtered)
24
+
25
+ ${f}`),v){const{stats:S,filtered:N}=E(v,`Phase ${e}`);u.push(`
26
+ ---
27
+ ## Implementation Progress`,`**Overall:** ${S.done}/${S.total} (${S.percent}%)`,"",N,"",'Use `update_progress({ item: "...", done: true })` to tick items as you complete them.')}else e>0&&u.push(`
28
+ ---
29
+ ## Implementation Progress`,"No implementation.md found. Consider running `init_implementation({ from_spec: true })` to track progress across sessions.");return u.push(`
30
+ ---
31
+ ## Enforcement Rules`,'- **BEFORE using any @donotdev component:** call `lookup_symbol("ComponentName")`','- **AFTER building:** call `complete_phase({ files: [...], summary: "..." })` \u2014 validates + submits for review',"- **WHEN recording a lesson:** also add a `// GOTCHA: ...` comment in the relevant source file","- Tracked symbols so far: none (call lookup_symbol to add)"),!i&&!a&&u.push(`
32
+ No blueprint or agent definition found for this phase.`),{content:[{type:"text",text:u.join(`
33
+ `)}]}}if(s==="complete_phase"){const e=k();if(e.currentPhase===null)return{content:[{type:"text",text:"No active phase to complete."}],isError:!0};if(e.pendingReview)return{content:[{type:"text",text:"Phase already pending review. Ask the user to review the output, then call approve_phase()."}],isError:!0};const n=e.currentPhase,o=e.phaseName,t=e.currentModule?` [${e.currentModule}]`:"",i=c?.files||[],a=[],p=[];if(i.length>0){for(const b of i){const A=pe(b);if(!A){a.push(`- BLOCKED: "${b}" resolves outside project root. Skipped.`);continue}p.push(A)}for(let b=0;b<p.length;b++){const A=we(p[b]);A.length>0&&a.push(`
34
+ ### ${i[b]}`,...A.map(ne=>`- ${ne}`))}const y=be(p,e.lookedUpSymbols);y.length>0&&(a.push(`
35
+ ### Unverified Component Usage`),a.push(...y.map(b=>`- ${b}`)))}const f=V();if(f){const y=te(f,n);y&&a.push(`
36
+ ### Done Criteria`,`**Required:** ${y.done_when}`,`**Expected output:** ${y.output}`,"","Verify these criteria are met.")}if(a.some(y=>y.startsWith("- L")||y.includes("Unverified Component")||y.includes("BLOCKED")))return{content:[{type:"text",text:[`## Validation Failed: Phase ${n} (${o})${t}`,"","**Status:** ISSUES FOUND \u2014 fix before completing phase",...a,"","### Type Check","Run `bunx tsc --noEmit` to verify TypeScript compiles without errors.","","Fix the issues above, then call `complete_phase({ files: [...] })` again."].join(`
37
+ `)}]};const u=c?.lesson;u&&Q(u,` [Phase ${n}: ${o}]`);const g=c?.summary;e.pendingLogData={files_touched:p.length,summary:g},e.pendingReview=!0,U(e);const v=e.lookedUpSymbols.length,P=C.find(y=>y.id===n+1),R=P?`Next: Phase ${P.id} (${P.name})`:"This was the final phase.";let _="";const S=O();if(S){const{stats:y}=E(S,`Phase ${n}`);_=`**Implementation:** ${y.done}/${y.total} items (${y.percent}%)`}return{content:[{type:"text",text:[`## Phase ${n} (${o})${t} \u2014 Ready for Review`,"",`**Validation:** ${i.length>0?"PASSED":"No files provided (skipped)"}`,`**Symbols looked up:** ${v} (${e.lookedUpSymbols.join(", ")||"none"})`,_,u?`**Lesson recorded:** ${u}`:"","","### User Review Required","","Present the phase output to the user. Ask them to verify:","- Does the output match what they expected?","- Are there any issues or changes needed?","","**After user confirms:** call `approve_phase()` to advance.","**If user wants changes:** make the changes, then call `complete_phase({ files: [...] })` again.","",R].filter(Boolean).join(`
38
+ `)}]}}if(s==="approve_phase"){const e=k();if(!e.pendingReview||e.currentPhase===null)return{content:[{type:"text",text:"No phase pending review. Call complete_phase() first."}],isError:!0};const n=e.currentPhase,o=e.phaseName,t=M();he({id:t.sessions.length+1,date:new Date().toISOString().split("T")[0],phase:n,phase_name:o||C[n]?.name||"UNKNOWN",module:e.currentModule||void 0,started_at:e.startedAt||new Date().toISOString(),completed_at:new Date().toISOString(),outcome:e.pendingLogData?.summary||`Phase ${n} (${o}) completed.`,files_touched:e.pendingLogData?.files_touched||0,symbols_used:e.lookedUpSymbols.length}),e.completedPhases.includes(n)||e.completedPhases.push(n),e.currentPhase=null,e.phaseName=null,e.agent=null,e.startedAt=null,e.pendingReview=!1,e.lookedUpSymbols=[],e.currentModule=null,e.pendingLogData=void 0,U(e);const i=C.find(p=>p.id===n+1),a=i?`Next: call start_phase(${i.id}) to begin ${i.name}.`:"All phases complete. App is ready.";return i&&fe(i.id,i.name),{content:[{type:"text",text:`Phase ${n} (${o}) approved and completed.
39
+ ${a}`}]}}if(s==="get_phase_status"){const e=k(),n=e.completedPhases.sort().map(t=>{const i=C.find(a=>a.id===t);return i?`${i.id}: ${i.name}`:`${t}`}).join(", ");return e.currentPhase===null?{content:[{type:"text",text:`No active phase.
40
+ Completed: ${n||"none"}
41
+ Call start_phase(N) to begin a phase.`}]}:{content:[{type:"text",text:[`Active: Phase ${e.currentPhase} (${e.phaseName})`,`Agent: ${e.agent}`,`Started: ${e.startedAt}`,e.currentModule?`Module: ${e.currentModule}`:null,`Pending review: ${e.pendingReview?"YES \u2014 user must approve":"no"}`,`Symbols looked up: ${e.lookedUpSymbols.length} (${e.lookedUpSymbols.join(", ")||"none"})`,`Completed phases: ${n||"none"}`].filter(Boolean).join(`
42
+ `)}]}}if(s==="get_project_history"){const e=M();if(e.sessions.length===0)return{content:[{type:"text",text:"No sessions logged yet. Captain's log entries are created automatically when phases are approved."}]};const n=e.sessions.reduce((a,p)=>a+p.files_touched,0),o=e.sessions.reduce((a,p)=>a+p.symbols_used,0),t=[...new Set(e.sessions.map(a=>a.phase))].length,i=[`## Captain's Log \u2014 ${e.project}`,`**Started:** ${e.started}`,`**Sessions:** ${e.sessions.length}`,`**Phases completed:** ${t}/5`,`**Total files touched:** ${n}`,`**Total symbols used:** ${o}`,"","### Timeline"];for(const a of e.sessions){const p=a.module?` [${a.module}]`:"";i.push(`${a.id}. **[${a.date}]** Phase ${a.phase} (${a.phase_name})${p} \u2014 ${a.outcome}`)}return i.push("","Raw JSON: .dndev/captain-log.json"),{content:[{type:"text",text:i.join(`
43
+ `)}]}}if(s==="record_lesson"){const e=c?.lesson,n=c?.tags,o=k(),t=o.currentPhase!==null?` [Phase ${o.currentPhase}: ${o.phaseName}]`:"";return Q(e,t,n),{content:[{type:"text",text:`Lesson recorded${n&&n.length>0?` [${n.join(", ")}]`:""}. Filtered by tags on next start_phase().
44
+ Also add a \`// GOTCHA: ...\` comment in the relevant source file.`}]}}if(s==="init_implementation"){const e=c?.sections,n=c?.from_spec,o=c?.force;if(m(j)&&!o){const p=O(),{stats:f}=E(p);return{content:[{type:"text",text:`implementation.md already exists (${f.done}/${f.total} items done, ${f.percent}%).
45
+ Pass force: true to overwrite, or use update_progress/get_progress to work with existing file.`}]}}let t;if(n){const p=me();if(!p)return{content:[{type:"text",text:"Could not generate from spec: spec_template.md not found in guides/wai-way/."}],isError:!0};t=p}else if(e&&e.length>0)t=e;else return{content:[{type:"text",text:"Provide sections (array of {title, items}) or set from_spec: true."}],isError:!0};const i=de(t);K(i);const{stats:a}=E(i);return{content:[{type:"text",text:`implementation.md created with ${a.total} items across ${t.length} sections.
46
+
47
+ ${i}`}]}}if(s==="update_progress"){const e=c?.item,n=c?.done,o=c?.note,t=O();if(!t)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const i=t.split(`
48
+ `),a=e.toLowerCase();let p=!1,f="";for(let g=0;g<i.length;g++){const v=i[g];if(/^\s*- \[[ x]\]/i.test(v)&&v.toLowerCase().includes(a)){n?i[g]=v.replace(/- \[ \]/,"- [x]"):i[g]=v.replace(/- \[x\]/i,"- [ ]"),f=i[g],p=!0;break}}if(!p)return{content:[{type:"text",text:`No checklist item matching "${e}" found in implementation.md.`}],isError:!0};let d=i.join(`
49
+ `);if(d=d.replace(/\*\*Last updated:\*\*\s*.*/,`**Last updated:** ${new Date().toISOString()}`),o){const g=`- ${o}`;d.includes("## Notes")?d=d.replace("## Notes",`## Notes
50
+ ${g}`):d+=`
51
+ ## Notes
52
+ ${g}
53
+ `}K(d);const{stats:u}=E(d);return{content:[{type:"text",text:`Updated: ${f.trim()}
54
+ Progress: ${u.done}/${u.total} (${u.percent}%)`}]}}if(s==="get_progress"){const e=c?.section,n=O();if(!n)return{content:[{type:"text",text:"No implementation.md found. Call init_implementation() first."}],isError:!0};const{stats:o,sections:t,filtered:i}=E(n,e),a=["## Implementation Progress",`**Overall:** ${o.done}/${o.total} (${o.percent}%)`,""];if(t.length>0){a.push("### By Section");for(const p of t){const f=p.total>0?Math.round(p.done/p.total*100):0;a.push(`- **${p.title}:** ${p.done}/${p.total} (${f}%)`)}a.push("")}return a.push("---","",i),{content:[{type:"text",text:a.join(`
55
+ `)}]}}if(!l&&!["get_guide","get_guideline"].includes(s))return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};if(s==="get_guide"){const e=c?.topic,n=ee();if(!n)return{content:[{type:"text",text:"Error: guides directory not found."}],isError:!0};const o=T(n).filter(t=>t.endsWith(".md")||t.endsWith(".md.example"));for(const t of o)if(t.toLowerCase().includes(e.toLowerCase()))return{content:[{type:"text",text:`[GUIDE] ${e} (from ${t}):
56
+
57
+ ${$(h(n,t),"utf-8")}`}]};return{content:[{type:"text",text:`No guide found for: ${e}`}],isError:!0}}if(s==="get_guideline"){const e=c?.topic,[n,o]=e.split(":"),t=[{dir:H,type:"GUIDE"},{dir:le,type:"ARCHITECTURE"}];for(const i of t){if(!m(i.dir))continue;const a=T(i.dir).filter(p=>p.endsWith(".md")||p.endsWith(".md.example"));for(const p of a)if(n&&p.toLowerCase().includes(n.toLowerCase())){const f=$(h(i.dir,p),"utf-8");if(!o){const u=f.match(/^([\s\S]*?)(?=##|$)/),g=u&&u[1]?u[1].trim():"No overview found.";return{content:[{type:"text",text:`[${i.type}] Overview for ${n}:
58
+
59
+ ${g}`}]}}const d=f.match(new RegExp(`##\\s+.*${o}.*\\n([\\s\\S]*?)(?=##|$)`,"i"));if(d&&d[1])return{content:[{type:"text",text:`[${i.type}] ${n} > ${o}:
60
+
61
+ ${d[1].trim()}`}]}}}return{content:[{type:"text",text:`No guideline found for: ${e}`}],isError:!0}}if(s==="search_framework"){const e=(c?.query).toLowerCase(),n=[],o=ee();if(o){const t=T(o).filter(i=>i.endsWith(".md")||i.endsWith(".md.example"));for(const i of t)$(h(o,i),"utf-8").toLowerCase().includes(e)&&n.push(`[GUIDE] ${i}`)}if(l)for(const t of J){const i=h(l,t,"dist");if(m(i)){for(const a of z(i))if($(a,"utf-8").toLowerCase().includes(e)){const p=xe($(a,"utf-8")).filter(f=>f.toLowerCase().includes(e));p.length>0&&n.push(`[SYMBOL] ${t}: ${p.join(", ")}`)}}}return{content:[{type:"text",text:n.length>0?`Results:
62
+ ${n.join(`
63
+ `)}`:"No results found."}]}}if(s==="lookup_symbol"){const e=c?.symbol;if(!l)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const n=k();n.lookedUpSymbols.includes(e)||(n.lookedUpSymbols.push(e),U(n));const o=[e,`${e}Props`,`${e.replace(/Props$/,"")}Props`],t=["@donotdev/core","@donotdev/ui","@donotdev/crud"],i=[...t,...J.filter(a=>!t.includes(a))];for(const a of i){const p=h(l,a,"dist");if(m(p))for(const f of z(p)){const d=$(f,"utf-8");for(const u of o){const g=Se(d,u);if(g)return{content:[{type:"text",text:`[TYPE INTELLIGENCE] ${a}:
64
+
65
+ ${g}`}]}}}}return{content:[{type:"text",text:`Not found: ${e}`}],isError:!0}}if(s==="list_features"){if(!l)return{content:[{type:"text",text:"Error: node_modules not found. Run bun install first."}],isError:!0};const e=[`## Available Framework Features
66
+ `];for(const n of ye){const o=h(l,n,"README.md"),t=ve(o);e.push(`- **${n}** \u2014 ${t||"(no description available)"}`)}return e.push('\nUse `search_framework("topic")` or `get_guide("TOPIC")` to go deeper on any package.'),{content:[{type:"text",text:e.join(`
67
+ `)}]}}return{content:[{type:"text",text:`Unknown tool: ${s}`}],isError:!0}});async function Pe(){const r=new se;await X.connect(r)}Pe().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/mcp-server",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "MCP server for DoNotDev component type lookups",
5
5
  "type": "module",
6
6
  "private": false,