@portel/photon 1.33.0 → 1.33.2

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.
Files changed (65) hide show
  1. package/README.md +43 -6
  2. package/dist/auth/mcp-jwt.d.ts +59 -0
  3. package/dist/auth/mcp-jwt.d.ts.map +1 -0
  4. package/dist/auth/mcp-jwt.js +177 -0
  5. package/dist/auth/mcp-jwt.js.map +1 -0
  6. package/dist/auto-ui/beam.d.ts +1 -0
  7. package/dist/auto-ui/beam.d.ts.map +1 -1
  8. package/dist/auto-ui/beam.js +35 -1
  9. package/dist/auto-ui/beam.js.map +1 -1
  10. package/dist/auto-ui/frontend/pure-view.html +5 -2
  11. package/dist/auto-ui/playground-html.d.ts.map +1 -1
  12. package/dist/auto-ui/playground-html.js +28 -38
  13. package/dist/auto-ui/playground-html.js.map +1 -1
  14. package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
  15. package/dist/auto-ui/streamable-http-transport.js +62 -11
  16. package/dist/auto-ui/streamable-http-transport.js.map +1 -1
  17. package/dist/beam.bundle.js +25 -17
  18. package/dist/beam.bundle.js.map +2 -2
  19. package/dist/capability-negotiator.d.ts +11 -0
  20. package/dist/capability-negotiator.d.ts.map +1 -1
  21. package/dist/capability-negotiator.js +20 -0
  22. package/dist/capability-negotiator.js.map +1 -1
  23. package/dist/cli/commands/auth.d.ts +15 -0
  24. package/dist/cli/commands/auth.d.ts.map +1 -0
  25. package/dist/cli/commands/auth.js +105 -0
  26. package/dist/cli/commands/auth.js.map +1 -0
  27. package/dist/cli/commands/host.d.ts.map +1 -1
  28. package/dist/cli/commands/host.js +9 -0
  29. package/dist/cli/commands/host.js.map +1 -1
  30. package/dist/cli/commands/run.d.ts.map +1 -1
  31. package/dist/cli/commands/run.js +3 -0
  32. package/dist/cli/commands/run.js.map +1 -1
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +6 -0
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/daemon/worker-dep-proxy.d.ts +17 -0
  37. package/dist/daemon/worker-dep-proxy.d.ts.map +1 -0
  38. package/dist/daemon/worker-dep-proxy.js +92 -0
  39. package/dist/daemon/worker-dep-proxy.js.map +1 -0
  40. package/dist/daemon/worker-host.js +8 -28
  41. package/dist/daemon/worker-host.js.map +1 -1
  42. package/dist/deploy/cloudflare.d.ts +2 -0
  43. package/dist/deploy/cloudflare.d.ts.map +1 -1
  44. package/dist/deploy/cloudflare.js +135 -13
  45. package/dist/deploy/cloudflare.js.map +1 -1
  46. package/dist/editor-support/docblock-tag-catalog.d.ts.map +1 -1
  47. package/dist/editor-support/docblock-tag-catalog.js +6 -0
  48. package/dist/editor-support/docblock-tag-catalog.js.map +1 -1
  49. package/dist/loader.d.ts +3 -0
  50. package/dist/loader.d.ts.map +1 -1
  51. package/dist/loader.js +49 -0
  52. package/dist/loader.js.map +1 -1
  53. package/dist/photon-doc-extractor.d.ts +1 -0
  54. package/dist/photon-doc-extractor.d.ts.map +1 -1
  55. package/dist/photon-doc-extractor.js +13 -0
  56. package/dist/photon-doc-extractor.js.map +1 -1
  57. package/dist/resource-server.d.ts +15 -0
  58. package/dist/resource-server.d.ts.map +1 -1
  59. package/dist/resource-server.js +86 -5
  60. package/dist/resource-server.js.map +1 -1
  61. package/dist/server.d.ts.map +1 -1
  62. package/dist/server.js +168 -176
  63. package/dist/server.js.map +1 -1
  64. package/package.json +1 -1
  65. package/templates/cloudflare/worker.ts.template +340 -55
package/README.md CHANGED
@@ -10,13 +10,15 @@
10
10
  [![Node](https://img.shields.io/badge/node-%3E%3D20-43853d.svg)](https://nodejs.org)
11
11
  [![MCP](https://img.shields.io/badge/MCP-compatible-7c3aed.svg)](https://modelcontextprotocol.io)
12
12
 
13
- ### Define intent once. Deliver everywhere.
13
+ ### Build MCP apps from TypeScript methods.
14
14
 
15
- Photon turns a single TypeScript file into:
15
+ Define the capability once. Photon turns it into the interfaces people now
16
+ expect around AI tools:
16
17
 
17
- - **MCP server** for AI agents
18
- - **CLI tool** for automation
19
- - **Web interface** for humans
18
+ - **MCP server** for Claude, ChatGPT, Cursor, and agents
19
+ - **Embedded app UI** for chat clients that support MCP app resources
20
+ - **CLI tool** for scripts, demos, and automation
21
+ - **Beam web interface** for humans
20
22
 
21
23
  Photon is free and open source software released under the [MIT license](./LICENSE).
22
24
 
@@ -24,6 +26,36 @@ Photon is free and open source software released under the [MIT license](./LICEN
24
26
 
25
27
  ---
26
28
 
29
+ ## From one method to every surface
30
+
31
+ The weather example is intentionally small: one TypeScript method, a few
32
+ docblock tags, and one `@ui` HTML asset. Photon turns that into a CLI command,
33
+ Beam UI, MCP tool, and embedded app surface for MCP app-capable chat clients.
34
+ Claude Desktop can run it from a local stdio MCP command; ChatGPT developer
35
+ mode can connect to the same Photon over a public HTTPS `/mcp` endpoint.
36
+
37
+ <div align="center">
38
+ <img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/photon-surface-map.svg" alt="Infographic showing one Photon method becoming CLI, Beam, MCP, Claude Desktop, ChatGPT, and other agent surfaces" width="100%">
39
+ </div>
40
+
41
+ <div align="center">
42
+ <img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/chat-clients-photon-weather-showcase.gif" alt="Animated Photon weather showcase with matched ChatGPT and Claude Desktop MCP app demos" width="100%">
43
+ </div>
44
+
45
+ <div align="center">
46
+ <img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/weather-showcase-hero.png" alt="Photon weather showcase: one method rendered across Beam, CLI, and chat app surfaces" width="100%">
47
+ </div>
48
+
49
+ [Follow the step-by-step tutorial](./docs/tutorials/from-method-to-chat-app.md)
50
+ or open the runnable example in
51
+ [`examples/weather-showcase`](./examples/weather-showcase).
52
+ Real-client proof is included for
53
+ [Claude Desktop](https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/claude-weather-real.png)
54
+ and
55
+ [ChatGPT developer mode](https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/chatgpt-photon-weather-local-dot.png).
56
+
57
+ ---
58
+
27
59
  ## One definition. Multiple interfaces.
28
60
 
29
61
  <div align="center">
@@ -550,6 +582,8 @@ Uses Bun's compiler under the hood. The binary bundles the photon, its `@depende
550
582
  | `@param ... {@format email}` | Param | Input validation and field type |
551
583
  | `@param ... {@min N} {@max N}` | Param | Numeric range constraints |
552
584
  | `@ui` | Class/Method | Link a custom HTML template |
585
+ | `@auth` | Class | Require or describe MCP authentication and populate `this.caller` |
586
+ | `@scope` | Method | Override the inferred OAuth scope for a protected MCP tool call |
553
587
  | `@expose` | Method | Auto-bind to `POST /api/<kebab>` for SPA fetch (`public` skips the SameSite gate) |
554
588
  | `@get /path` | Method | HTTP-only GET route; shown as a web app in Beam, not an MCP tool. Supports `:param` segments |
555
589
  | `@post /path` | Method | HTTP-only POST route; shown as a web app in Beam, not an MCP tool. Supports `:param` segments |
@@ -573,7 +607,9 @@ Uses Bun's compiler under the hood. The binary bundles the photon, its `@depende
573
607
  | Guide | |
574
608
  |---|---|
575
609
  | [Getting Started](./docs/getting-started.md) | Install, build, and run your first photon in 5 minutes |
610
+ | [From Method to Chat App](./docs/tutorials/from-method-to-chat-app.md) | Weather showcase: CLI, Beam, MCP, and embedded app UI from one method |
576
611
  | [Core Concepts](./docs/concepts.md) | The 6 ideas behind Photon |
612
+ | [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) | Public reference for every docblock tag Photon understands |
577
613
  | [Output Formats](./docs/formats.md) | Visual gallery of every `@format` type |
578
614
  | [Intent Metadata](./docs/reference/INTENT-METADATA.md) | How comments, schemas, annotations, and formats map to native surfaces |
579
615
  | [Settings](./docs/GUIDE.md#settings-user-configurable-knobs) | Declare runtime knobs with `protected settings` (the canonical config pattern) |
@@ -585,6 +621,7 @@ Uses Bun's compiler under the hood. The binary bundles the photon, its `@depende
585
621
  |---|---|
586
622
  | [Custom UI](./docs/guides/CUSTOM-UI.md) | Build rich interactive interfaces with the photon bridge API |
587
623
  | [OAuth](./docs/guides/AUTH.md) | Built-in OAuth 2.0 with Google, GitHub, Microsoft |
624
+ | [MCP JWT Auth](./docs/guides/MCP-JWT-AUTH.md) | Secure deployed MCP tool calls with short-lived scoped JWTs |
588
625
  | [MCP Client Registration](./docs/guides/mcp-client-registration.md) | Register MCP clients with Photon's AS via CIMD or DCR |
589
626
  | [Observability](./docs/guides/observability.md) | OpenTelemetry traces, metrics, logs, and structured errors |
590
627
  | [Protocol Features](./docs/guides/PROTOCOL-FEATURES.md) | Capability handshake, structured errors, trace correlation |
@@ -603,7 +640,7 @@ Uses Bun's compiler under the hood. The binary bundles the photon, its `@depende
603
640
  | [Marketplace Publishing](./docs/guides/MARKETPLACE-PUBLISHING.md) | Create and share team marketplaces |
604
641
  | [Best Practices](./docs/guides/BEST-PRACTICES.md) | Patterns for production photons |
605
642
 
606
- **Reference:** [Complete Developer Guide](./docs/GUIDE.md) · [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) · [Naming Conventions](./docs/guides/NAMING-CONVENTIONS.md) · [Architecture](./docs/internals/ARCHITECTURE.md) · [OAuth Authorization Server](./docs/internals/OAUTH-AUTHORIZATION-SERVER.md) · [Lifecycle & Ingress](./docs/internals/LIFECYCLE-AND-INGRESS.md) · [PHOTON_DIR & Namespace](./docs/internals/PHOTON-DIR-AND-NAMESPACE.md) · [Changelog](./CHANGELOG.md) · [Contributing](./CONTRIBUTING.md)
643
+ **Reference:** [Complete Developer Guide](./docs/GUIDE.md) · [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) · [Naming Conventions](./docs/guides/NAMING-CONVENTIONS.md) · [Architecture](./docs/internals/ARCHITECTURE.md) · [Lifecycle & Ingress](./docs/internals/LIFECYCLE-AND-INGRESS.md) · [PHOTON_DIR & Namespace](./docs/internals/PHOTON-DIR-AND-NAMESPACE.md) · [Changelog](./CHANGELOG.md) · [Contributing](./CONTRIBUTING.md)
607
644
 
608
645
  ---
609
646
 
@@ -0,0 +1,59 @@
1
+ import { type JsonWebKey } from 'node:crypto';
2
+ export interface PhotonAuthIssuer {
3
+ issuer: string;
4
+ algorithm: 'ES256';
5
+ kid: string;
6
+ defaultTtlSeconds: number;
7
+ }
8
+ export interface PhotonAuthTokenOptions {
9
+ agent: string;
10
+ audience: string;
11
+ tenant?: string;
12
+ scopes?: string[];
13
+ ttlSeconds?: number;
14
+ now?: Date;
15
+ jti?: string;
16
+ }
17
+ export interface PhotonAuthVerifyOptions {
18
+ issuer: string;
19
+ audience: string;
20
+ jwks: {
21
+ keys: JsonWebKey[];
22
+ };
23
+ now?: Date;
24
+ clockSkewSeconds?: number;
25
+ requiredScopes?: string[];
26
+ }
27
+ export type PhotonJwtVerifyReason = 'missing_token' | 'malformed_token' | 'unsupported_alg' | 'unknown_kid' | 'bad_signature' | 'expired_token' | 'token_not_yet_valid' | 'wrong_issuer' | 'wrong_audience' | 'insufficient_scope' | 'tenant_mismatch';
28
+ export interface PhotonJwtClaims {
29
+ iss: string;
30
+ sub: string;
31
+ aud: string | string[];
32
+ tenant_id: string;
33
+ client_id: string;
34
+ scope?: string;
35
+ iat: number;
36
+ nbf: number;
37
+ exp: number;
38
+ jti: string;
39
+ [key: string]: unknown;
40
+ }
41
+ export type PhotonJwtVerifyResult = {
42
+ ok: true;
43
+ claims: PhotonJwtClaims;
44
+ } | {
45
+ ok: false;
46
+ reason: PhotonJwtVerifyReason;
47
+ };
48
+ export declare function createPhotonAuthKeypair(name: string, now?: Date): {
49
+ issuer: PhotonAuthIssuer;
50
+ privateJwk: JsonWebKey;
51
+ publicJwk: JsonWebKey;
52
+ jwks: {
53
+ keys: JsonWebKey[];
54
+ };
55
+ };
56
+ export declare function signPhotonAuthToken(issuer: PhotonAuthIssuer, privateJwk: JsonWebKey, options: PhotonAuthTokenOptions): string;
57
+ export declare function verifyPhotonAuthToken(token: string | null | undefined, options: PhotonAuthVerifyOptions): PhotonJwtVerifyResult;
58
+ export declare function normalizeScopes(scopes: string[]): string[];
59
+ //# sourceMappingURL=mcp-jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-jwt.d.ts","sourceRoot":"","sources":["../../src/auth/mcp-jwt.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAC7B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,qBAAqB,GAC7B,eAAe,GACf,iBAAiB,GACjB,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,eAAe,GACf,qBAAqB,GACrB,cAAc,GACd,gBAAgB,GAChB,oBAAoB,GACpB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAEjD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,GAAG,OAAa,GACf;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,UAAU,CAAC;IACtB,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;CAC9B,CAsBA;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,sBAAsB,GAC9B,MAAM,CAiBR;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,OAAO,EAAE,uBAAuB,GAC/B,qBAAqB,CA4CvB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAY1D"}
@@ -0,0 +1,177 @@
1
+ import { createPrivateKey, createPublicKey, createSign, createVerify, generateKeyPairSync, randomBytes, } from 'node:crypto';
2
+ export function createPhotonAuthKeypair(name, now = new Date()) {
3
+ const date = now.toISOString().slice(0, 10);
4
+ const kid = `${name}-${date}`;
5
+ const { privateKey, publicKey } = generateKeyPairSync('ec', { namedCurve: 'P-256' });
6
+ const privateJwk = privateKey.export({ format: 'jwk' });
7
+ const publicJwk = publicKey.export({ format: 'jwk' });
8
+ for (const jwk of [privateJwk, publicJwk]) {
9
+ jwk.alg = 'ES256';
10
+ jwk.kid = kid;
11
+ jwk.use = 'sig';
12
+ }
13
+ return {
14
+ issuer: {
15
+ issuer: `photon-local:${name}`,
16
+ algorithm: 'ES256',
17
+ kid,
18
+ defaultTtlSeconds: 15 * 60,
19
+ },
20
+ privateJwk,
21
+ publicJwk,
22
+ jwks: { keys: [publicJwk] },
23
+ };
24
+ }
25
+ export function signPhotonAuthToken(issuer, privateJwk, options) {
26
+ const nowSec = Math.floor((options.now?.getTime() ?? Date.now()) / 1000);
27
+ const ttl = options.ttlSeconds ?? issuer.defaultTtlSeconds;
28
+ const scopes = normalizeScopes(options.scopes ?? []);
29
+ const payload = {
30
+ iss: issuer.issuer,
31
+ sub: `agent:${options.agent}`,
32
+ aud: options.audience,
33
+ tenant_id: options.tenant ?? 'default',
34
+ client_id: options.agent,
35
+ ...(scopes.length > 0 ? { scope: scopes.join(' ') } : {}),
36
+ iat: nowSec,
37
+ nbf: nowSec,
38
+ exp: nowSec + ttl,
39
+ jti: options.jti ?? `tok_${randomBytes(16).toString('base64url')}`,
40
+ };
41
+ return signJwt(payload, privateJwk, issuer.kid);
42
+ }
43
+ export function verifyPhotonAuthToken(token, options) {
44
+ if (!token)
45
+ return { ok: false, reason: 'missing_token' };
46
+ const parts = token.split('.');
47
+ if (parts.length !== 3)
48
+ return { ok: false, reason: 'malformed_token' };
49
+ let header;
50
+ let claims;
51
+ try {
52
+ header = JSON.parse(base64UrlDecode(parts[0]));
53
+ claims = JSON.parse(base64UrlDecode(parts[1]));
54
+ }
55
+ catch {
56
+ return { ok: false, reason: 'malformed_token' };
57
+ }
58
+ if (header.alg !== 'ES256')
59
+ return { ok: false, reason: 'unsupported_alg' };
60
+ const kid = typeof header.kid === 'string' ? header.kid : '';
61
+ const jwk = options.jwks.keys.find((key) => key.kid === kid);
62
+ if (!kid || !jwk)
63
+ return { ok: false, reason: 'unknown_kid' };
64
+ if (!verifyJwtSignature(parts, jwk))
65
+ return { ok: false, reason: 'bad_signature' };
66
+ if (claims.iss !== options.issuer)
67
+ return { ok: false, reason: 'wrong_issuer' };
68
+ const audMatches = Array.isArray(claims.aud)
69
+ ? claims.aud.includes(options.audience)
70
+ : claims.aud === options.audience;
71
+ if (!audMatches)
72
+ return { ok: false, reason: 'wrong_audience' };
73
+ const now = Math.floor((options.now?.getTime() ?? Date.now()) / 1000);
74
+ const skew = options.clockSkewSeconds ?? 60;
75
+ if (typeof claims.exp !== 'number' || claims.exp < now - skew) {
76
+ return { ok: false, reason: 'expired_token' };
77
+ }
78
+ if (typeof claims.nbf === 'number' && claims.nbf > now + skew) {
79
+ return { ok: false, reason: 'token_not_yet_valid' };
80
+ }
81
+ if (typeof claims.iat === 'number' && claims.iat > now + skew) {
82
+ return { ok: false, reason: 'token_not_yet_valid' };
83
+ }
84
+ const required = normalizeScopes(options.requiredScopes ?? []);
85
+ if (required.length > 0) {
86
+ const granted = new Set(normalizeScopes((claims.scope ?? '').split(/\s+/)));
87
+ for (const scope of required) {
88
+ if (!granted.has(scope))
89
+ return { ok: false, reason: 'insufficient_scope' };
90
+ }
91
+ }
92
+ return { ok: true, claims };
93
+ }
94
+ export function normalizeScopes(scopes) {
95
+ const out = [];
96
+ const seen = new Set();
97
+ for (const raw of scopes) {
98
+ for (const scope of raw.split(/\s+/)) {
99
+ const trimmed = scope.trim();
100
+ if (!trimmed || seen.has(trimmed))
101
+ continue;
102
+ seen.add(trimmed);
103
+ out.push(trimmed);
104
+ }
105
+ }
106
+ return out;
107
+ }
108
+ function signJwt(payload, privateJwk, kid) {
109
+ const header = { alg: 'ES256', typ: 'JWT', kid };
110
+ const input = `${base64UrlEncode(JSON.stringify(header))}.${base64UrlEncode(JSON.stringify(payload))}`;
111
+ const key = createPrivateKey({ key: privateJwk, format: 'jwk' });
112
+ const signer = createSign('sha256');
113
+ signer.update(input);
114
+ signer.end();
115
+ const der = signer.sign(key);
116
+ return `${input}.${derToP1363(der, 32).toString('base64url')}`;
117
+ }
118
+ function verifyJwtSignature(parts, publicJwk) {
119
+ try {
120
+ const key = createPublicKey({ key: publicJwk, format: 'jwk' });
121
+ const verifier = createVerify('sha256');
122
+ verifier.update(`${parts[0]}.${parts[1]}`);
123
+ verifier.end();
124
+ return verifier.verify(key, p1363ToDer(Buffer.from(parts[2], 'base64url')));
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ }
130
+ function base64UrlEncode(str) {
131
+ return Buffer.from(str, 'utf-8').toString('base64url');
132
+ }
133
+ function base64UrlDecode(str) {
134
+ return Buffer.from(str, 'base64url').toString('utf-8');
135
+ }
136
+ function derToP1363(der, componentLen) {
137
+ if (der[0] !== 0x30)
138
+ throw new Error('invalid DER signature');
139
+ let offset = 2;
140
+ if ((der[1] & 0x80) !== 0)
141
+ offset += der[1] & 0x7f;
142
+ const readInt = () => {
143
+ if (der[offset] !== 0x02)
144
+ throw new Error('invalid DER signature');
145
+ const len = der[offset + 1];
146
+ const start = offset + 2;
147
+ let value = der.subarray(start, start + len);
148
+ offset = start + len;
149
+ while (value.length > 1 && value[0] === 0x00)
150
+ value = value.subarray(1);
151
+ if (value.length > componentLen)
152
+ throw new Error('ECDSA component overflow');
153
+ return value;
154
+ };
155
+ const r = readInt();
156
+ const s = readInt();
157
+ const out = Buffer.alloc(componentLen * 2);
158
+ r.copy(out, componentLen - r.length);
159
+ s.copy(out, componentLen * 2 - s.length);
160
+ return out;
161
+ }
162
+ function p1363ToDer(p1363) {
163
+ if (p1363.length % 2 !== 0)
164
+ throw new Error('invalid P1363 signature');
165
+ const half = p1363.length / 2;
166
+ const encodeInt = (value) => {
167
+ let v = value;
168
+ while (v.length > 1 && v[0] === 0x00)
169
+ v = v.subarray(1);
170
+ if ((v[0] & 0x80) !== 0)
171
+ v = Buffer.concat([Buffer.from([0x00]), v]);
172
+ return Buffer.concat([Buffer.from([0x02, v.length]), v]);
173
+ };
174
+ const body = Buffer.concat([encodeInt(p1363.subarray(0, half)), encodeInt(p1363.subarray(half))]);
175
+ return Buffer.concat([Buffer.from([0x30, body.length]), body]);
176
+ }
177
+ //# sourceMappingURL=mcp-jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-jwt.js","sourceRoot":"","sources":["../../src/auth/mcp-jwt.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@ export declare function shouldServeLinkedAppForWebPath(pathname: string, searchP
16
16
  export declare function selectClientAppUi(photonInfo: PhotonInfo | undefined): string | undefined;
17
17
  export declare function shouldFallbackToClientAppForWebPath(pathname: string, searchParams: URLSearchParams, route: BeamWebRouteDef | undefined): boolean;
18
18
  export declare function findBeamWebRoute(routes: BeamWebRouteDef[] | undefined, method: string, requestPath: string): BeamWebRouteDef | undefined;
19
+ export declare function rewriteBeamWebRedirectLocation(location: string | null, prefix: string, requestOrigin: string): string | null;
19
20
  import type { PhotonInfo } from './types.js';
20
21
  export type { PhotonConfig } from './beam/types.js';
21
22
  export type { BeamState } from './beam/types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"beam.d.ts","sourceRoot":"","sources":["../../src/auto-ui/beam.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAenF;AAYD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,GAC5B,OAAO,CAIT;AAUD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAmBxF;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,OAAO,CAIT;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,eAAe,EAAE,GAAG,SAAS,EACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,eAAe,GAAG,SAAS,CAO7B;AA4GD,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,YAAY,CAAC;AAsCpB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAqYjD,wBAAsB,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA05FlF;AAED;;;GAGG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9C"}
1
+ {"version":3,"file":"beam.d.ts","sourceRoot":"","sources":["../../src/auto-ui/beam.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAenF;AAYD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,GAC5B,OAAO,CAIT;AAUD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAmBxF;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,OAAO,CAIT;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,eAAe,EAAE,GAAG,SAAS,EACrC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,eAAe,GAAG,SAAS,CAO7B;AAwCD,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CAaf;AAsED,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,YAAY,CAAC;AAsCpB,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAqYjD,wBAAsB,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAi7FlF;AAED;;;GAGG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9C"}
@@ -165,6 +165,22 @@ async function writeFetchResponseToNode(response, res) {
165
165
  res.off('close', cancelReader);
166
166
  }
167
167
  }
168
+ export function rewriteBeamWebRedirectLocation(location, prefix, requestOrigin) {
169
+ if (!location)
170
+ return null;
171
+ try {
172
+ const target = new URL(location, requestOrigin);
173
+ if (target.origin !== requestOrigin)
174
+ return location;
175
+ if (target.pathname === prefix || target.pathname.startsWith(`${prefix}/`)) {
176
+ return target.pathname + target.search + target.hash;
177
+ }
178
+ return `${prefix}${target.pathname}${target.search}${target.hash}`;
179
+ }
180
+ catch {
181
+ return location;
182
+ }
183
+ }
168
184
  /**
169
185
  * Resolve raw icon image paths to MCP Icon[] format (data URIs)
170
186
  */
@@ -2125,6 +2141,25 @@ export async function startBeam(rawWorkingDir, port) {
2125
2141
  });
2126
2142
  const result = await fn.call(photonClass.instance, webReq);
2127
2143
  if (result instanceof Response) {
2144
+ const prefix = `/web/${photonName}`;
2145
+ if (result.status >= 300 && result.status < 400) {
2146
+ const responseHeaders = {};
2147
+ result.headers.forEach((value, key) => {
2148
+ if (key.toLowerCase() !== 'location')
2149
+ responseHeaders[key] = value;
2150
+ });
2151
+ const location = rewriteBeamWebRedirectLocation(result.headers.get('location'), prefix, internalUrl.origin);
2152
+ if (location) {
2153
+ responseHeaders.location = location;
2154
+ }
2155
+ const body = Buffer.from(await result.arrayBuffer());
2156
+ if (body.length > 0) {
2157
+ responseHeaders['content-length'] = String(body.length);
2158
+ }
2159
+ res.writeHead(result.status, responseHeaders);
2160
+ res.end(body.length > 0 ? body : undefined);
2161
+ return;
2162
+ }
2128
2163
  const contentType = result.headers.get('content-type') || '';
2129
2164
  if (!contentType.includes('text/html')) {
2130
2165
  await writeFetchResponseToNode(result, res);
@@ -2137,7 +2172,6 @@ export async function startBeam(rawWorkingDir, port) {
2137
2172
  responseHeaders[key] = value;
2138
2173
  });
2139
2174
  let body = Buffer.from(await result.arrayBuffer());
2140
- const prefix = `/web/${photonName}`;
2141
2175
  const interceptor = `<script>
2142
2176
  (function(){
2143
2177
  const _prefix="${prefix}";