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:

  • [ ] scripts/pre-release-check.sh passes before tagging or publishing
  • [ ] tests/daemon-chaos.test.ts passes; daemon spawn races block release
  • [ ] tests/contract/render-dom.test.ts passes with Playwright Chromium installed
  • [ ] 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.