Photon Development Best Practices
Guidelines for creating high-quality, consistent Photon MCPs.
1. JSDoc Header Tags
Every Photon file must include these tags in the class-level JSDoc:
Required Tags
typescript
/**
* My Photon - Brief description
*
* Detailed description of what this photon does.
* Include common use cases and examples.
*
* @version 1.0.0
* @author Portel
* @license MIT
*/| Tag | Description | Example |
|---|---|---|
@version | Semantic version | @version 1.0.0 |
@author | Author name | @author Portel |
@license | License type | @license MIT |
Conditional Tags
| Tag | When to Use | Example |
|---|---|---|
@dependencies | External npm packages | @dependencies axios@^1.6.0, cheerio@^1.0.0 |
@runtime | Using newer runtime features (io, elicitation) | @runtime ^1.4.0 |
@tags | Categorization for discovery | @tags commerce, payments, api |
@stateful | Maintains state across calls | @stateful true |
@idleTimeout | Custom idle timeout (ms) | @idleTimeout 600000 |
Example: Full Header
typescript
/**
* Shopping Cart - AI-powered e-commerce assistant
*
* Demonstrates the AI+Human transaction workflow:
* 1. AI suggests products based on query
* 2. Human reviews and selects items
* 3. Human confirms purchase
*
* Example: search({ query: "wireless headphones" })
*
* @version 1.0.0
* @author Portel
* @license MIT
* @runtime ^1.4.0
* @dependencies axios@^1.7.0
* @tags commerce, ai-assistant, elicitation
*/2. Method Naming Conventions
Methods should read like natural English when called via CLI.
Principles
- Avoid redundancy - Don't repeat the class name in methods
- Use concise verbs - Single words when possible
- Standard CRUD -
create,get,list,update,delete - Singular vs plural - Singular for one item, plural for collections
Examples
| Bad | Good | CLI Usage |
|---|---|---|
getUsers() | users() | user-service.users |
getUserById() | user() | user-service.user |
createUser() | create() | user-service.create |
deleteUser() | delete() | user-service.delete |
getCurrentTime() | now() | time.now |
listTimezones() | timezones() | time.timezones |
addToCart() | add() | shop.add |
viewCart() | cart() | shop.cart |
applyDiscount() | discount() | shop.discount |
Standard Verbs
| Action | Verb | Example |
|---|---|---|
| Get single item | get or noun | user(), product() |
| List items | plural noun | users(), products() |
| Create | create, add, new | create(), add() |
| Update | update, set | update(), set() |
| Delete | delete, remove | delete(), remove() |
| Search | search, find | search(), find() |
| Execute | run, execute | run(), execute() |
3. Method Documentation
Every public method needs proper JSDoc.
Required Elements
typescript
/**
* Brief description of what the method does
*
* @param paramName Description of parameter
* @format list {@title name, @subtitle description}
*/
async users(params?: { limit?: number }): Promise<User[]>Parameter Documentation
typescript
/**
* Search products in catalog
*
* @param query Search query string
* @param category Filter by category {@example "electronics"}
* @param limit Max results (default: 10) {@min 1} {@max 100}
*/
async search(params: {
query: string;
category?: string;
limit?: number;
}): Promise<Product[]>Format Hints
| Format | Use Case |
|---|---|
@format list | Array of objects |
@format table | Tabular data |
@format card | Single object |
@format json | Raw JSON |
@format markdown | Markdown content |
@format html | HTML content |
@format none | No rendering (actions) |
UI Template Linking
typescript
/**
* Dashboard main view
*
* @icon 📋
* @ui tasks
*/
async main(): Promise<Task[]>4. Code Organization
File Structure
my-photon.photon.ts # Main photon file
my-photon/
ui/
tasks.html # Custom UI templates
overview.html
data/
config.json # Persistent dataClass Structure
typescript
export default class MyPhoton {
// 1. Private fields
private data: Map<string, any>;
// 2. Constructor
constructor(config?: string) { }
// 3. Lifecycle hooks
async onInitialize() { }
// 4. Public methods (alphabetical or grouped by feature)
async methodA() { }
async methodB() { }
// 5. Private helpers (prefixed with _)
private _helper() { }
}Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Class | PascalCase | ShoppingCart |
| Methods | camelCase | addItem() |
| Private methods | _prefix | _validateInput() |
| Constants | UPPER_SNAKE | MAX_ITEMS |
| Interfaces | PascalCase | CartItem |
5. Error Handling
Throw Descriptive Errors
typescript
// Good
if (!cart) {
throw new Error(`Cart not found: ${cartId}`);
}
// Bad
if (!cart) {
throw new Error('Not found');
}Validate Input Early
typescript
async update(params: { id: string; quantity: number }) {
if (!params.id) {
throw new Error('Product ID is required');
}
if (params.quantity < 0) {
throw new Error('Quantity must be non-negative');
}
// ... proceed with valid input
}6. Return Value Guidelines
Consistent Response Structure
typescript
// For actions that might fail
return {
success: true,
message: 'Item added to cart',
data: { cartId, itemCount }
};
// For queries
return {
items: [...],
total: 42,
page: 1
};Use Formatted Strings for Display
typescript
// Good - formatted for display
return {
price: `$${amount.toFixed(2)}`,
date: new Date(timestamp).toLocaleDateString()
};
// Bad - raw values
return {
price: 29.99,
date: '2024-01-15T10:30:00Z'
};7. State Management
Persist Important Data
typescript
const DATA_FILE = path.join(os.homedir(), '.photon', 'my-photon', 'data.json');
private async load(): Promise<Data> {
try {
const content = await fs.readFile(DATA_FILE, 'utf-8');
return JSON.parse(content);
} catch {
return DEFAULT_DATA;
}
}
private async save(data: Data): Promise<void> {
await fs.mkdir(path.dirname(DATA_FILE), { recursive: true });
await fs.writeFile(DATA_FILE, JSON.stringify(data, null, 2));
}Mark Stateful Photons
typescript
/**
* @stateful true
* @idleTimeout 600000
*/8. Testing Checklist
Before release, verify:
- [ ] All methods have JSDoc with descriptions
- [ ] All parameters are documented
- [ ] Error cases throw descriptive errors
- [ ] Return values are properly formatted
- [ ] State persists correctly (if applicable)
- [ ] No hardcoded paths (use
os.homedir()) - [ ] Dependencies are declared in header
- [ ] Version, author, license tags present
9. Quick Reference
Minimal Photon Template
typescript
/**
* My Photon - Brief description
*
* @version 1.0.0
* @author Portel
* @license MIT
*/
export default class MyPhoton {
/**
* Main entry point
*/
async main(): Promise<string> {
return 'Hello from MyPhoton!';
}
}Full-Featured Template
typescript
/**
* My Photon - Comprehensive description
*
* Detailed explanation of features:
* - Feature 1
* - Feature 2
*
* Example: main({ option: "value" })
*
* @version 1.0.0
* @author Portel
* @license MIT
* @runtime ^1.4.0
* @dependencies axios@^1.7.0
* @tags category1, category2
*/
import { io } from '@portel/photon-core';
interface Item {
id: string;
name: string;
}
export default class MyPhoton {
private items: Item[] = [];
async onInitialize() {
// Setup logic
}
/**
* List all items
* @format list {@title name}
*/
async items(): Promise<Item[]> {
return this.items;
}
/**
* Add a new item
* @param name Item name
*/
async add(params: { name: string }): Promise<Item> {
const item = { id: String(Date.now()), name: params.name };
this.items.push(item);
return item;
}
}