Skip to content

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
 */
TagDescriptionExample
@versionSemantic version@version 1.0.0
@authorAuthor name@author Portel
@licenseLicense type@license MIT

Conditional Tags

TagWhen to UseExample
@dependenciesExternal npm packages@dependencies axios@^1.6.0, cheerio@^1.0.0
@runtimeUsing newer runtime features (io, elicitation)@runtime ^1.4.0
@tagsCategorization for discovery@tags commerce, payments, api
@statefulMaintains state across calls@stateful true
@idleTimeoutCustom 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

  1. Avoid redundancy - Don't repeat the class name in methods
  2. Use concise verbs - Single words when possible
  3. Standard CRUD - create, get, list, update, delete
  4. Singular vs plural - Singular for one item, plural for collections

Examples

BadGoodCLI 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

ActionVerbExample
Get single itemget or nounuser(), product()
List itemsplural nounusers(), products()
Createcreate, add, newcreate(), add()
Updateupdate, setupdate(), set()
Deletedelete, removedelete(), remove()
Searchsearch, findsearch(), find()
Executerun, executerun(), 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

FormatUse Case
@format listArray of objects
@format tableTabular data
@format cardSingle object
@format jsonRaw JSON
@format markdownMarkdown content
@format htmlHTML content
@format noneNo 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 data

Class 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

ElementConventionExample
ClassPascalCaseShoppingCart
MethodscamelCaseaddItem()
Private methods_prefix_validateInput()
ConstantsUPPER_SNAKEMAX_ITEMS
InterfacesPascalCaseCartItem

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!';
  }
}
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;
  }
}

Released under the MIT License.