npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@portel/photon-core

v1.0.2

Published

Core library for parsing, loading, and managing .photon.ts files - runtime-agnostic foundation for building custom Photon runtimes

Readme

@portel/photon-core

Core library for parsing, loading, and managing .photon.ts files

npm version License: MIT

🎯 What is Photon Core?

Photon Core is the runtime-agnostic foundation for working with .photon.ts files. It provides:

  • Base class with lifecycle hooks (onInitialize, onShutdown)
  • Dependency management - Extract and install @dependencies from JSDoc
  • Schema extraction - Parse TypeScript types and JSDoc into JSON schemas
  • NO runtime layers - No MCP server, no CLI, no transport

This allows you to build custom runtimes on top of the Photon format:

  • Multi-protocol API servers (REST, GraphQL, RPC, MCP) → Lumina
  • OrchestratorsNCP
  • MCP/CLI runners@portel/photon

📦 Installation

npm install @portel/photon-core

🚀 Quick Start

1. Create a Photon class

// calculator.photon.ts

/**
 * Simple calculator with basic math operations
 * @dependencies lodash@^4.17.21
 */
export default class Calculator {
  /**
   * Add two numbers together
   * @param a First number
   * @param b Second number
   */
  async add(params: { a: number; b: number }) {
    return params.a + params.b;
  }

  /**
   * Multiply two numbers
   * @param a First number
   * @param b Second number
   */
  async multiply(params: { a: number; b: number }) {
    return params.a * params.b;
  }

  /**
   * Lifecycle hook called when photon is initialized
   */
  async onInitialize() {
    console.log('Calculator initialized!');
  }

  /**
   * Lifecycle hook called when photon is shut down
   */
  async onShutdown() {
    console.log('Calculator shutting down!');
  }
}

2. Use Photon Core to load and manage it

import { PhotonMCP, DependencyManager, SchemaExtractor } from '@portel/photon-core';

// 1. Extract and install dependencies
const depManager = new DependencyManager();
const deps = await depManager.extractDependencies('./calculator.photon.ts');
// Found: [{ name: 'lodash', version: '^4.17.21' }]

await depManager.ensureDependencies('calculator', deps);
// ✅ Dependencies installed

// 2. Load the class
const CalculatorClass = (await import('./calculator.photon.ts')).default;
const instance = new CalculatorClass();

// 3. Call lifecycle hook
if (instance.onInitialize) {
  await instance.onInitialize();
}

// 4. Extract schemas (optional)
const extractor = new SchemaExtractor();
const schemas = await extractor.extractFromFile('./calculator.photon.ts');
console.log(schemas);
// [
//   {
//     name: 'add',
//     description: 'Add two numbers together',
//     inputSchema: {
//       type: 'object',
//       properties: {
//         a: { type: 'number', description: 'First number' },
//         b: { type: 'number', description: 'Second number' }
//       },
//       required: ['a', 'b']
//     }
//   },
//   ...
// ]

// 5. Execute methods
const result = await instance.add({ a: 5, b: 3 });
console.log(result); // 8

// 6. Cleanup
if (instance.onShutdown) {
  await instance.onShutdown();
}

📚 API Reference

PhotonMCP

Base class for creating Photon classes (optional - you can use plain classes too).

import { PhotonMCP } from '@portel/photon-core';

export default class MyPhoton extends PhotonMCP {
  async myMethod(params: { input: string }) {
    return `Hello ${params.input}`;
  }

  async onInitialize() {
    // Called when photon is loaded
  }

  async onShutdown() {
    // Called when photon is unloaded
  }
}

Static methods:

  • getMCPName() - Convert class name to kebab-case (e.g., MyAwesomeMCPmy-awesome-mcp)
  • getToolMethods() - Get all public async methods (excludes lifecycle hooks and private methods)

Instance methods:

  • executeTool(name, params) - Execute a method by name

Lifecycle hooks:

  • onInitialize() - Called when photon is initialized
  • onShutdown() - Called when photon is shut down

DependencyManager

Extracts and installs npm dependencies declared in JSDoc @dependencies tags.

import { DependencyManager } from '@portel/photon-core';

const depManager = new DependencyManager();

// Extract dependencies from source file
const deps = await depManager.extractDependencies('./my-tool.photon.ts');
// [{ name: 'axios', version: '^1.0.0' }, { name: 'date-fns', version: '^2.0.0' }]

// Install dependencies to isolated cache
await depManager.ensureDependencies('my-tool', deps);
// Creates ~/.cache/photon-mcp/dependencies/my-tool/node_modules/

// Clear cache for a specific photon
await depManager.clearCache('my-tool');

// Clear all caches
await depManager.clearAllCache();

Supported JSDoc format:

/**
 * @dependencies axios@^1.0.0, date-fns@^2.0.0
 * @dependencies @octokit/rest@^19.0.0
 */

SchemaExtractor

Extracts JSON schemas from TypeScript method signatures and JSDoc comments.

import { SchemaExtractor } from '@portel/photon-core';

const extractor = new SchemaExtractor();

// Extract from file
const schemas = await extractor.extractFromFile('./my-tool.photon.ts');

// Extract from source string
const source = await fs.readFile('./my-tool.photon.ts', 'utf-8');
const schemas = extractor.extractFromSource(source);

// Extract constructor parameters
const constructorParams = extractor.extractConstructorParams(source);

Schema format:

interface ExtractedSchema {
  name: string;
  description: string;
  inputSchema: {
    type: 'object';
    properties: Record<string, any>;
    required?: string[];
  };
}

Supported JSDoc constraints:

/**
 * @param age User age {@min 0} {@max 120}
 * @param email Email address {@format email}
 * @param username Username {@pattern ^[a-z0-9_]+$}
 * @param count Count {@default 10}
 */

🏗️ Building Custom Runtimes

Photon Core is designed to be the foundation for custom runtimes. Here are examples:

Example 1: REST API Server

import express from 'express';
import { PhotonMCP, DependencyManager, SchemaExtractor } from '@portel/photon-core';

class PhotonRESTServer {
  async serve(photonPath: string) {
    const app = express();

    // Load photon
    const PhotonClass = (await import(photonPath)).default;
    const instance = new PhotonClass();
    await instance.onInitialize?.();

    // Extract schemas
    const extractor = new SchemaExtractor();
    const schemas = await extractor.extractFromFile(photonPath);

    // Create REST endpoints
    schemas.forEach(schema => {
      app.post(`/api/${schema.name}`, async (req, res) => {
        try {
          const result = await instance[schema.name](req.body);
          res.json({ result });
        } catch (error) {
          res.status(500).json({ error: error.message });
        }
      });
    });

    app.listen(3000);
  }
}

Example 2: GraphQL Server

import { buildSchema } from 'graphql';
import { PhotonMCP, SchemaExtractor } from '@portel/photon-core';

class PhotonGraphQLServer {
  async generateSchema(photonPath: string) {
    const extractor = new SchemaExtractor();
    const schemas = await extractor.extractFromFile(photonPath);

    const mutations = schemas.map(s =>
      `${s.name}(${this.paramsToGraphQL(s.inputSchema)}): JSON`
    ).join('\n');

    return buildSchema(`
      type Mutation {
        ${mutations}
      }
    `);
  }
}

Example 3: MCP Orchestrator

import { PhotonMCP, DependencyManager } from '@portel/photon-core';

class PhotonOrchestrator {
  private photons = new Map();

  async loadPhoton(path: string) {
    // Install dependencies
    const depManager = new DependencyManager();
    const deps = await depManager.extractDependencies(path);
    const name = basename(path, '.photon.ts');
    await depManager.ensureDependencies(name, deps);

    // Load class
    const PhotonClass = (await import(path)).default;
    const instance = new PhotonClass();
    await instance.onInitialize?.();

    this.photons.set(name, instance);
  }

  async execute(photonName: string, method: string, params: any) {
    const photon = this.photons.get(photonName);
    return await photon[method](params);
  }
}

🎯 Use Cases

| Project | Description | Runtime Layer | |---------|-------------|---------------| | @portel/photon | CLI tool for running Photons as MCP servers or CLI tools | MCP server (stdio), CLI runner | | NCP | Multi-MCP orchestrator with discovery and semantic search | Orchestrator, unified MCP interface | | Lumina | Multi-protocol API server | REST, GraphQL, RPC, HTTP streaming MCP |

All three projects use @portel/photon-core for the shared format, but each adds its own runtime layer.


🔧 Development

# Clone the repository
git clone https://github.com/portel-dev/photon-core.git
cd photon-core

# Install dependencies
npm install

# Build
npm run build

# Test
npm test

# Lint
npm run lint

📄 License

MIT © Portel


🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


🔗 Related Projects


📖 Documentation

For more information about the Photon format and ecosystem: