@portel/photon 1.33.1 → 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.
- package/README.md +38 -5
- package/dist/auto-ui/frontend/pure-view.html +5 -2
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +62 -11
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/beam.bundle.js +19 -17
- package/dist/beam.bundle.js.map +2 -2
- package/dist/capability-negotiator.d.ts +11 -0
- package/dist/capability-negotiator.d.ts.map +1 -1
- package/dist/capability-negotiator.js +20 -0
- package/dist/capability-negotiator.js.map +1 -1
- package/dist/daemon/worker-dep-proxy.d.ts +17 -0
- package/dist/daemon/worker-dep-proxy.d.ts.map +1 -0
- package/dist/daemon/worker-dep-proxy.js +92 -0
- package/dist/daemon/worker-dep-proxy.js.map +1 -0
- package/dist/daemon/worker-host.js +8 -28
- package/dist/daemon/worker-host.js.map +1 -1
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +15 -0
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/resource-server.d.ts +15 -0
- package/dist/resource-server.d.ts.map +1 -1
- package/dist/resource-server.js +86 -5
- package/dist/resource-server.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +20 -10
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/templates/cloudflare/worker.ts.template +26 -8
package/README.md
CHANGED
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
[](https://modelcontextprotocol.io)
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### Build MCP apps from TypeScript methods.
|
|
14
14
|
|
|
15
|
-
Photon turns
|
|
15
|
+
Define the capability once. Photon turns it into the interfaces people now
|
|
16
|
+
expect around AI tools:
|
|
16
17
|
|
|
17
|
-
- **MCP server** for
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
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">
|
|
@@ -575,6 +607,7 @@ Uses Bun's compiler under the hood. The binary bundles the photon, its `@depende
|
|
|
575
607
|
| Guide | |
|
|
576
608
|
|---|---|
|
|
577
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 |
|
|
578
611
|
| [Core Concepts](./docs/concepts.md) | The 6 ideas behind Photon |
|
|
579
612
|
| [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) | Public reference for every docblock tag Photon understands |
|
|
580
613
|
| [Output Formats](./docs/formats.md) | Visual gallery of every `@format` type |
|
|
@@ -236,9 +236,12 @@
|
|
|
236
236
|
// Handle JSON-RPC tools/call from bridge and form components
|
|
237
237
|
if (msg.jsonrpc === '2.0' && msg.method === 'tools/call' && msg.id != null) {
|
|
238
238
|
var rawName = (msg.params && msg.params.name) || '';
|
|
239
|
-
// Don't double-prefix: invoke-form sends fully-qualified names (photon
|
|
239
|
+
// Don't double-prefix: invoke-form sends fully-qualified names (photon.method),
|
|
240
240
|
// bridge sends unqualified names (just method). Check for prefix before adding.
|
|
241
|
-
var toolName =
|
|
241
|
+
var toolName =
|
|
242
|
+
rawName.indexOf('.') !== -1 || rawName.indexOf('/') !== -1
|
|
243
|
+
? rawName
|
|
244
|
+
: photonName + '.' + rawName;
|
|
242
245
|
var args = (msg.params && msg.params.arguments) || {};
|
|
243
246
|
|
|
244
247
|
var doCall = function() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamable-http-transport.d.ts","sourceRoot":"","sources":["../../src/auto-ui/streamable-http-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAuB5D,OAAO,KAAK,EAOV,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"streamable-http-transport.d.ts","sourceRoot":"","sources":["../../src/auto-ui/streamable-http-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -148,6 +148,33 @@ function decodeJWTCaller(authHeader) {
|
|
|
148
148
|
return undefined;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
function isOpenAIAppSession(session) {
|
|
152
|
+
const name = session.clientInfo?.name?.toLowerCase();
|
|
153
|
+
return name === 'chatgpt' || !!name?.includes('openai');
|
|
154
|
+
}
|
|
155
|
+
function namespacedToolName(serverName, methodName) {
|
|
156
|
+
return `${serverName}.${methodName}`;
|
|
157
|
+
}
|
|
158
|
+
function toolNameForSession(session, photonName, methodName) {
|
|
159
|
+
return isOpenAIAppSession(session) ? methodName : namespacedToolName(photonName, methodName);
|
|
160
|
+
}
|
|
161
|
+
function splitNamespacedToolName(name) {
|
|
162
|
+
const dotIndex = name.indexOf('.');
|
|
163
|
+
if (dotIndex !== -1) {
|
|
164
|
+
return {
|
|
165
|
+
serverName: name.slice(0, dotIndex),
|
|
166
|
+
methodName: name.slice(dotIndex + 1),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const slashIndex = name.indexOf('/');
|
|
170
|
+
if (slashIndex !== -1) {
|
|
171
|
+
return {
|
|
172
|
+
serverName: name.slice(0, slashIndex),
|
|
173
|
+
methodName: name.slice(slashIndex + 1),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
151
178
|
// ════════════════════════════════════════════════════════════════════════════════
|
|
152
179
|
// SESSION MANAGEMENT
|
|
153
180
|
// ════════════════════════════════════════════════════════════════════════════════
|
|
@@ -1150,7 +1177,7 @@ const handlers = {
|
|
|
1150
1177
|
: undefined;
|
|
1151
1178
|
const meta = buildToolMCPMeta(method, { uiResourceUri });
|
|
1152
1179
|
tools.push({
|
|
1153
|
-
name:
|
|
1180
|
+
name: toolNameForSession(session, photon.name, method.name),
|
|
1154
1181
|
description: method.description || `Execute ${method.name}`,
|
|
1155
1182
|
inputSchema: method.params || { type: 'object', properties: {} },
|
|
1156
1183
|
'x-photon-id': photon.id, // Unique ID (hash of path) for subscriptions
|
|
@@ -1186,7 +1213,7 @@ const handlers = {
|
|
|
1186
1213
|
if (!photon.configured || !photon.stateful)
|
|
1187
1214
|
continue;
|
|
1188
1215
|
tools.push({
|
|
1189
|
-
name:
|
|
1216
|
+
name: namespacedToolName(photon.name, '_use'),
|
|
1190
1217
|
description: `Switch to a named instance of ${photon.name}. Omit name to select interactively.`,
|
|
1191
1218
|
inputSchema: {
|
|
1192
1219
|
type: 'object',
|
|
@@ -1201,21 +1228,21 @@ const handlers = {
|
|
|
1201
1228
|
'x-photon-internal': true,
|
|
1202
1229
|
});
|
|
1203
1230
|
tools.push({
|
|
1204
|
-
name:
|
|
1231
|
+
name: namespacedToolName(photon.name, '_instances'),
|
|
1205
1232
|
description: `List all available instances of ${photon.name}.`,
|
|
1206
1233
|
inputSchema: { type: 'object', properties: {} },
|
|
1207
1234
|
'x-photon-id': photon.id,
|
|
1208
1235
|
'x-photon-internal': true,
|
|
1209
1236
|
});
|
|
1210
1237
|
tools.push({
|
|
1211
|
-
name:
|
|
1238
|
+
name: namespacedToolName(photon.name, '_undo'),
|
|
1212
1239
|
description: `Undo the last state mutation on ${photon.name}. Reverts the most recent change.`,
|
|
1213
1240
|
inputSchema: { type: 'object', properties: {} },
|
|
1214
1241
|
'x-photon-id': photon.id,
|
|
1215
1242
|
'x-photon-internal': true,
|
|
1216
1243
|
});
|
|
1217
1244
|
tools.push({
|
|
1218
|
-
name:
|
|
1245
|
+
name: namespacedToolName(photon.name, '_redo'),
|
|
1219
1246
|
description: `Redo the last undone mutation on ${photon.name}. Re-applies a previously undone change.`,
|
|
1220
1247
|
inputSchema: { type: 'object', properties: {} },
|
|
1221
1248
|
'x-photon-id': photon.id,
|
|
@@ -1230,7 +1257,7 @@ const handlers = {
|
|
|
1230
1257
|
for (const method of mcp.methods) {
|
|
1231
1258
|
const meta = buildToolMCPMeta(method, { uiResourceUri: method.linkedUi });
|
|
1232
1259
|
tools.push({
|
|
1233
|
-
name:
|
|
1260
|
+
name: namespacedToolName(mcp.name, method.name),
|
|
1234
1261
|
description: method.description || `Execute ${method.name}`,
|
|
1235
1262
|
inputSchema: method.params || { type: 'object', properties: {} },
|
|
1236
1263
|
'x-external-mcp': true, // Marker for frontend to identify external MCPs
|
|
@@ -1542,9 +1569,31 @@ const handlers = {
|
|
|
1542
1569
|
if (name === 'beam/studio-parse') {
|
|
1543
1570
|
return handleBeamStudioParse(req, args || {});
|
|
1544
1571
|
}
|
|
1545
|
-
// Parse tool name: server-name
|
|
1546
|
-
|
|
1547
|
-
|
|
1572
|
+
// Parse tool name: server-name.method-name.
|
|
1573
|
+
// ChatGPT/OpenAI app sessions receive slashless names in tools/list because
|
|
1574
|
+
// their connector layer treats slash-qualified names as app resource paths.
|
|
1575
|
+
let serverName;
|
|
1576
|
+
let methodName;
|
|
1577
|
+
const namespacedName = splitNamespacedToolName(name);
|
|
1578
|
+
if (!namespacedName && isOpenAIAppSession(session)) {
|
|
1579
|
+
const matches = ctx.photons.filter((photon) => photon.configured && photon.methods?.some((method) => method.name === name));
|
|
1580
|
+
if (matches.length === 1) {
|
|
1581
|
+
const photon = matches[0];
|
|
1582
|
+
serverName = photon.name;
|
|
1583
|
+
methodName = name;
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
return {
|
|
1587
|
+
jsonrpc: '2.0',
|
|
1588
|
+
id: req.id,
|
|
1589
|
+
result: {
|
|
1590
|
+
content: [{ type: 'text', text: `Invalid tool name: ${name}` }],
|
|
1591
|
+
isError: true,
|
|
1592
|
+
},
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
else if (!namespacedName) {
|
|
1548
1597
|
return {
|
|
1549
1598
|
jsonrpc: '2.0',
|
|
1550
1599
|
id: req.id,
|
|
@@ -1554,8 +1603,10 @@ const handlers = {
|
|
|
1554
1603
|
},
|
|
1555
1604
|
};
|
|
1556
1605
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1606
|
+
else {
|
|
1607
|
+
serverName = namespacedName.serverName;
|
|
1608
|
+
methodName = namespacedName.methodName;
|
|
1609
|
+
}
|
|
1559
1610
|
// Per-photon auth check: if this photon requires auth but caller is anonymous, reject
|
|
1560
1611
|
const targetPhoton = ctx.photons.find((p) => p.name === serverName);
|
|
1561
1612
|
// Claim-code scope enforcement: filtering tools/list alone is not a
|