@portel/photon-core 2.15.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/mixins.ts CHANGED
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  import { MCPClient, MCPClientFactory, createMCPProxy } from '@portel/mcp';
26
- import { executionContext } from '@portel/cli';
26
+ import { executionContext, type CallerInfo } from '@portel/cli';
27
27
  import { getBroker } from './channels/index.js';
28
28
  import { MemoryProvider } from './memory.js';
29
29
  import { ScheduleProvider } from './schedule.js';
@@ -94,6 +94,14 @@ export function withPhotonCapabilities<T extends Constructor>(Base: T): T {
94
94
  */
95
95
  private _mcpClients: Map<string, MCPClient & Record<string, (params?: any) => Promise<any>>> = new Map();
96
96
 
97
+ /**
98
+ * Authenticated caller identity from MCP OAuth
99
+ */
100
+ get caller(): CallerInfo {
101
+ const store = executionContext.getStore();
102
+ return store?.caller ?? { id: 'anonymous', anonymous: true };
103
+ }
104
+
97
105
  /**
98
106
  * Scoped key-value storage for photon data
99
107
  */
@@ -24,6 +24,13 @@ export interface ExtractedMetadata {
24
24
  configSchema?: ConfigSchema;
25
25
  /** Notification subscription from @notify-on tag */
26
26
  notificationSubscriptions?: NotificationSubscription;
27
+ /**
28
+ * MCP OAuth auth requirement (from @auth tag)
29
+ * - 'required': all methods require authenticated caller
30
+ * - 'optional': caller populated if token present, anonymous allowed
31
+ * - string URL: OIDC provider URL (implies required)
32
+ */
33
+ auth?: 'required' | 'optional' | string;
27
34
  }
28
35
 
29
36
  /**
@@ -64,6 +71,9 @@ export class SchemaExtractor {
64
71
  // Notification subscriptions tracking (from @notify-on tag)
65
72
  let notificationSubscriptions: NotificationSubscription | undefined;
66
73
 
74
+ // MCP OAuth auth requirement (from @auth tag)
75
+ let auth: 'required' | 'optional' | string | undefined;
76
+
67
77
  try {
68
78
  // If source doesn't contain a class declaration, wrap it in one
69
79
  let sourceToParse = source;
@@ -448,6 +458,12 @@ export class SchemaExtractor {
448
458
  const classJsdoc = this.getJSDocComment(node as any, sourceFile);
449
459
  const isStatefulClass = /@stateful\b/i.test(classJsdoc);
450
460
 
461
+ // Extract @auth tag for MCP OAuth requirement
462
+ const authMatch = classJsdoc.match(/@auth(?:\s+(\S+))?/i);
463
+ if (authMatch) {
464
+ auth = authMatch[1]?.trim() || 'required';
465
+ }
466
+
451
467
  // Extract notification subscriptions from @notify-on tag
452
468
  notificationSubscriptions = this.extractNotifyOn(classJsdoc);
453
469
 
@@ -495,6 +511,11 @@ export class SchemaExtractor {
495
511
  result.notificationSubscriptions = notificationSubscriptions;
496
512
  }
497
513
 
514
+ // Include auth requirement if detected
515
+ if (auth) {
516
+ result.auth = auth;
517
+ }
518
+
498
519
  return result;
499
520
  }
500
521
 
@@ -1220,7 +1241,7 @@ export class SchemaExtractor {
1220
1241
  private extractDescription(jsdocContent: string): string {
1221
1242
  // Split by @tags that appear at start of a JSDoc line (after optional * prefix)
1222
1243
  // This avoids matching @tag references inline in description text
1223
- const beforeTags = jsdocContent.split(/(?:^|\n)\s*\*?\s*@(?:param|example|returns?|throws?|see|since|deprecated|version|author|license|ui|icon|format|stateful|autorun|async|webhook|cron|scheduled|locked|fallback|logged|circuitBreaker|cached|timeout|retryable|throttled|debounced|queued|validate|use|Template|Static|mcp|photon|cli|tags|dependencies|csp|visibility)\b/)[0];
1244
+ const beforeTags = jsdocContent.split(/(?:^|\n)\s*\*?\s*@(?:param|example|returns?|throws?|see|since|deprecated|version|author|license|ui|icon|format|stateful|autorun|async|webhook|cron|scheduled|locked|fallback|logged|circuitBreaker|cached|timeout|retryable|throttled|debounced|queued|validate|use|Template|Static|mcp|photon|cli|tags|dependencies|csp|visibility|auth)\b/)[0];
1224
1245
 
1225
1246
  // Remove leading * from each line and trim
1226
1247
  const lines = beforeTags
@@ -2920,7 +2941,7 @@ export class SchemaExtractor {
2920
2941
  /**
2921
2942
  * Capability types that can be auto-detected from source code
2922
2943
  */
2923
- export type PhotonCapability = 'emit' | 'memory' | 'call' | 'mcp' | 'lock' | 'instanceMeta' | 'allInstances';
2944
+ export type PhotonCapability = 'emit' | 'memory' | 'call' | 'mcp' | 'lock' | 'instanceMeta' | 'allInstances' | 'caller';
2924
2945
 
2925
2946
  /**
2926
2947
  * Detect capabilities used by a Photon from its source code.
@@ -2941,5 +2962,6 @@ export function detectCapabilities(source: string): Set<PhotonCapability> {
2941
2962
  if (/this\.withLock\s*\(/.test(source)) caps.add('lock');
2942
2963
  if (/this\.instanceMeta\b/.test(source)) caps.add('instanceMeta');
2943
2964
  if (/this\.allInstances\s*\(/.test(source)) caps.add('allInstances');
2965
+ if (/this\.caller\b/.test(source)) caps.add('caller');
2944
2966
  return caps;
2945
2967
  }