Your First Project
Get a working project running in under 5 minutes. You'll have an AI that can create and edit notes.
Step 1: Create the Project
Run the create command and follow the prompts:
malv create
This scaffolds a complete project with a Notes app already set up. You get:
my-project/
├── malv.json
├── packages/
│ ├── apps/
│ │ └── notes/ # A working notes app
│ │ ├── tools.json # create_note, edit_note, list_notes
│ │ ├── tokens.json # note_access token
│ │ ├── storage.json # Storage permissions
│ │ └── src/ # Tool implementations
│ └── web/ # Web interface to test it
└── package.json
Step 2: Install Dependencies
cd my-project
yarn install
Step 3: Configure Environment
Set up the required environment variables:
malv env
This prompts you for API keys and configuration. The command reads what each app needs from its environment.json and helps you fill in the values.
Step 4: Start the Development Server
malv dev
You'll see your apps start up:
Starting apps...
@my-org/notes → http://localhost:4550
Infrastructure server running at http://localhost:59459
Web server running at http://localhost:3000
Watching for changes...
Step 5: Try It Out
Open http://localhost:3000 in your browser. You'll see a chat interface.
Try these messages:
Create a note called "Shopping List" with the content "Milk, eggs, bread"
The AI calls the create_note tool. You'll see a Note tab appear with your content.
Now try:
Add butter to the list
The AI calls edit_note. It knows which note you're working with because create_note issued a token that tracks the selected note.
What notes do I have?
The AI calls list_notes and shows you all your notes.
What Just Happened?
The Notes app demonstrates the core concepts:
Tools - create_note, edit_note, and list_notes are defined in tools.json. The AI discovered them automatically.
Tokens - When you create a note, the app issues a note_access token. This is why "add butter to the list" works without specifying which note. The AI sees you have a note selected.
Storage - Notes are saved to storage. The paths in storage.json control where data can be written.
Objects - The Note tab that appears is an object. It's defined in objects.json and rendered by custom code.
Explore the Code
Take a look at how the Notes app is built:
tools.json - Defines what tools exist and their input/output schemas:
{
"name": "create_note",
"description": "Creates a new note with the given title and content",
"capabilities": ["storage", "token", "object"],
...
}
tokens.json - Defines the note_access token that tracks which note is selected:
{
"note_access": {
"state": "Note selected",
"schema": {
"properties": {
"noteId": { "type": "string" }
}
}
}
}
src/tools/create_note.ts - The actual implementation:
export default async function create_note(
input: Input,
capabilities: Capabilities
): Promise<Output> {
const noteId = crypto.randomUUID();
// Save to storage
await capabilities.storage.put(`/notes/${noteId}.json`, {
title: input.title,
content: input.content
});
// Issue token so AI knows which note is selected
await capabilities.token.sign('note_access', { noteId });
// Create visual object
await capabilities.object.set('@my-org/notes', {
type: 'note',
name: input.title,
metadata: { noteId }
});
return { noteId, success: true };
}
Next Steps
Now that you have a working project:
Or try modifying the Notes app - add a delete_note tool and see how quickly you can get it working.