Build a local TypeScript server
Create the notes data
Define a small typed dataset that makes every tool result predictable before adding the protocol layer.
8 minute lesson
Before adding MCP, define the data our server is allowed to expose. Create src/notes.ts:
export type Note = {
id: string
title: string
tags: string[]
body: string
}
export const notes: Note[] = [
{
id: 'deploy-checklist',
title: 'Deploy checklist',
tags: ['deploy', 'release'],
body: 'Run tests, build the app, then verify the health check.'
},
{
id: 'incident-notes',
title: 'Incident notes',
tags: ['operations'],
body: 'Record the timeline, impact, cause, and follow-up work.'
},
{
id: 'review-guide',
title: 'Review guide',
tags: ['quality'],
body: 'Check behavior, tests, security boundaries, and documentation.'
}
]
This is deliberately boring data. Every search returns the same result, so a failure points to our contract or transport instead of an unreliable backend.
Notice what is missing. There is no file path, database credential, user ID, or private note. The dataset itself is part of the server’s security boundary.
We will expose summaries from search and complete bodies from lookup. That separation lets us keep discovery results small without making note IDs unstable.
Add one quick assertion while developing:
if (new Set(notes.map(note => note.id)).size !== notes.length) {
throw new Error('Note IDs must be unique')
}
A stable ID must identify one note for the lifetime of the public contract. Titles can change. IDs should not silently point to different content.
Later, you can replace this array with a database. Keep the same public schemas and rerun the same tests. That isolates backend changes from protocol changes.
Lesson completed