Your First Agent
After running cast init, you have a working agent. Let’s understand its structure and how to customize it.
Agent Structure
Every Castari agent has this structure:
my-agent/
├── castari.json # Agent configuration
├── package.json # Dependencies
├── tsconfig.json # TypeScript config
├── src/
│ └── index.ts # Agent entry point
├── CLAUDE.md # Agent instructions
└── README.md # Documentation
castari.json
The agent configuration file:
{
"name" : "my-agent" ,
"version" : "0.1.0" ,
"entrypoint" : "src/index.ts" ,
"runtime" : "node"
}
Field Description nameDisplay name for your agent versionSemantic version entrypointPath to main file runtimeRuntime environment (node)
CLAUDE.md
This file contains instructions for Claude. It’s like a system prompt that shapes your agent’s behavior:
# My Agent
You are a helpful coding assistant.
## Guidelines
- Be concise and direct
- Always explain before making changes
- Ask for clarification when requirements are unclear
## Tools Available
- `read_file` - Read file contents
- `write_file` - Create or update files
- `bash` - Run shell commands
The better your CLAUDE.md, the better your agent performs. Be specific about capabilities, constraints, and expected behavior.
src/index.ts
The entry point defines your agent’s tools and handles invocations:
import Anthropic from "@anthropic-ai/sdk" ;
const client = new Anthropic ();
// Define tools
const tools : Anthropic . Tool [] = [
{
name: "read_file" ,
description: "Read the contents of a file" ,
input_schema: {
type: "object" ,
properties: {
path: { type: "string" , description: "File path to read" }
},
required: [ "path" ]
}
},
// ... more tools
];
// Handle tool calls
async function handleTool ( name : string , input : any ) : Promise < string > {
switch ( name ) {
case "read_file" :
return fs . readFileSync ( input . path , "utf-8" );
// ... handle other tools
}
}
// Main agent loop
async function run ( prompt : string ) {
// Agent implementation
}
// Read prompt from stdin, run agent, output to stdout
let prompt = "" ;
process . stdin . on ( "data" , ( chunk ) => ( prompt += chunk ));
process . stdin . on ( "end" , async () => {
const response = await run ( prompt );
console . log ( response );
});
Local Testing
Test your agent locally before deploying:
cd my-agent
echo "What files are here?" | npm run dev
This runs your agent with the prompt as input, just like cast invoke does remotely.
Customizing Your Agent
Define the tool schema in tools array
Add the handler in handleTool function
// 1. Define the tool
{
name : "search_web" ,
description : "Search the web for information" ,
input_schema : {
type : "object" ,
properties : {
query : { type : "string" , description : "Search query" }
},
required : [ "query" ]
}
}
// 2. Handle the tool
case "search_web" :
return await searchWeb ( input . query );
Change Agent Behavior
Edit CLAUDE.md to change how your agent thinks and acts:
# Research Agent
You are a research assistant that finds and synthesizes information.
## Guidelines
- Always cite sources
- Present multiple perspectives
- Summarize findings at the end
Add Dependencies
npm install axios # Example: HTTP client
Then import and use in your agent:
import axios from 'axios' ;
// In your tool handler
const response = await axios . get ( 'https://api.example.com/data' );
Redeploying
After making changes, redeploy:
This uploads your changes and creates a fresh sandbox.
Redeploying destroys the previous sandbox. Any files or state in the sandbox are lost.
Next Steps
CLI Reference Learn all available commands
Templates Try different agent templates
Add Secrets Add API keys for external services
How It Works Understand the architecture