@sajou/mcp-server 0.6.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/README.md +78 -0
- package/dist/app.d.ts +32 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +76 -0
- package/dist/app.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/transport.d.ts +18 -0
- package/dist/mcp/transport.d.ts.map +1 -0
- package/dist/mcp/transport.js +56 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/middleware.d.ts +14 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +14 -0
- package/dist/middleware.js.map +1 -0
- package/dist/routes/discovery.d.ts +8 -0
- package/dist/routes/discovery.d.ts.map +1 -0
- package/dist/routes/discovery.js +155 -0
- package/dist/routes/discovery.js.map +1 -0
- package/dist/routes/proxy.d.ts +10 -0
- package/dist/routes/proxy.d.ts.map +1 -0
- package/dist/routes/proxy.js +84 -0
- package/dist/routes/proxy.js.map +1 -0
- package/dist/routes/scene.d.ts +10 -0
- package/dist/routes/scene.d.ts.map +1 -0
- package/dist/routes/scene.js +298 -0
- package/dist/routes/scene.js.map +1 -0
- package/dist/routes/signals.d.ts +12 -0
- package/dist/routes/signals.d.ts.map +1 -0
- package/dist/routes/signals.js +61 -0
- package/dist/routes/signals.js.map +1 -0
- package/dist/routes/tap.d.ts +10 -0
- package/dist/routes/tap.d.ts.map +1 -0
- package/dist/routes/tap.js +115 -0
- package/dist/routes/tap.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +103 -0
- package/dist/server.js.map +1 -0
- package/dist/state/mutations.d.ts +53 -0
- package/dist/state/mutations.d.ts.map +1 -0
- package/dist/state/mutations.js +409 -0
- package/dist/state/mutations.js.map +1 -0
- package/dist/state/store.d.ts +51 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +149 -0
- package/dist/state/store.js.map +1 -0
- package/dist/tools/create-binding.d.ts +45 -0
- package/dist/tools/create-binding.d.ts.map +1 -0
- package/dist/tools/create-binding.js +82 -0
- package/dist/tools/create-binding.js.map +1 -0
- package/dist/tools/create-choreography.d.ts +76 -0
- package/dist/tools/create-choreography.d.ts.map +1 -0
- package/dist/tools/create-choreography.js +118 -0
- package/dist/tools/create-choreography.js.map +1 -0
- package/dist/tools/create-shader.d.ts +117 -0
- package/dist/tools/create-shader.d.ts.map +1 -0
- package/dist/tools/create-shader.js +150 -0
- package/dist/tools/create-shader.js.map +1 -0
- package/dist/tools/create-sketch.d.ts +96 -0
- package/dist/tools/create-sketch.d.ts.map +1 -0
- package/dist/tools/create-sketch.js +130 -0
- package/dist/tools/create-sketch.js.map +1 -0
- package/dist/tools/create-wire.d.ts +32 -0
- package/dist/tools/create-wire.d.ts.map +1 -0
- package/dist/tools/create-wire.js +84 -0
- package/dist/tools/create-wire.js.map +1 -0
- package/dist/tools/describe-scene.d.ts +22 -0
- package/dist/tools/describe-scene.d.ts.map +1 -0
- package/dist/tools/describe-scene.js +194 -0
- package/dist/tools/describe-scene.js.map +1 -0
- package/dist/tools/emit-signal.d.ts +36 -0
- package/dist/tools/emit-signal.d.ts.map +1 -0
- package/dist/tools/emit-signal.js +60 -0
- package/dist/tools/emit-signal.js.map +1 -0
- package/dist/tools/get-catalog.d.ts +27 -0
- package/dist/tools/get-catalog.d.ts.map +1 -0
- package/dist/tools/get-catalog.js +37 -0
- package/dist/tools/get-catalog.js.map +1 -0
- package/dist/tools/get-choreographies.d.ts +21 -0
- package/dist/tools/get-choreographies.d.ts.map +1 -0
- package/dist/tools/get-choreographies.js +70 -0
- package/dist/tools/get-choreographies.js.map +1 -0
- package/dist/tools/get-scene-state.d.ts +21 -0
- package/dist/tools/get-scene-state.d.ts.map +1 -0
- package/dist/tools/get-scene-state.js +48 -0
- package/dist/tools/get-scene-state.js.map +1 -0
- package/dist/tools/get-shaders.d.ts +16 -0
- package/dist/tools/get-shaders.d.ts.map +1 -0
- package/dist/tools/get-shaders.js +38 -0
- package/dist/tools/get-shaders.js.map +1 -0
- package/dist/tools/get-sketches.d.ts +16 -0
- package/dist/tools/get-sketches.d.ts.map +1 -0
- package/dist/tools/get-sketches.js +37 -0
- package/dist/tools/get-sketches.js.map +1 -0
- package/dist/tools/list-themes.d.ts +21 -0
- package/dist/tools/list-themes.d.ts.map +1 -0
- package/dist/tools/list-themes.js +32 -0
- package/dist/tools/list-themes.js.map +1 -0
- package/dist/tools/map-signals.d.ts +30 -0
- package/dist/tools/map-signals.d.ts.map +1 -0
- package/dist/tools/map-signals.js +44 -0
- package/dist/tools/map-signals.js.map +1 -0
- package/dist/tools/place-entity.d.ts +47 -0
- package/dist/tools/place-entity.d.ts.map +1 -0
- package/dist/tools/place-entity.js +85 -0
- package/dist/tools/place-entity.js.map +1 -0
- package/dist/tools/remove-item.d.ts +25 -0
- package/dist/tools/remove-item.d.ts.map +1 -0
- package/dist/tools/remove-item.js +55 -0
- package/dist/tools/remove-item.js.map +1 -0
- package/dist/tools/set-sketch-param.d.ts +29 -0
- package/dist/tools/set-sketch-param.d.ts.map +1 -0
- package/dist/tools/set-sketch-param.js +49 -0
- package/dist/tools/set-sketch-param.js.map +1 -0
- package/dist/tools/set-uniform.d.ts +29 -0
- package/dist/tools/set-uniform.d.ts.map +1 -0
- package/dist/tools/set-uniform.js +49 -0
- package/dist/tools/set-uniform.js.map +1 -0
- package/dist/tools/update-shader.d.ts +119 -0
- package/dist/tools/update-shader.d.ts.map +1 -0
- package/dist/tools/update-shader.js +81 -0
- package/dist/tools/update-shader.js.map +1 -0
- package/dist/tools/update-sketch.d.ts +99 -0
- package/dist/tools/update-sketch.d.ts.map +1 -0
- package/dist/tools/update-sketch.js +75 -0
- package/dist/tools/update-sketch.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @sajou/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for sajou. Lets AI agents interact with the visual choreographer via the standard MCP protocol.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
### 1. Start the scene-builder dev server
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm --filter scene-builder dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The scene-builder runs on `http://localhost:5175` by default.
|
|
14
|
+
|
|
15
|
+
### 2. Configure your MCP client
|
|
16
|
+
|
|
17
|
+
#### Claude Code
|
|
18
|
+
|
|
19
|
+
Add to your MCP config (`~/.claude/claude_desktop_config.json` or project `.mcp.json`):
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"sajou": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "@sajou/mcp-server"],
|
|
27
|
+
"env": {
|
|
28
|
+
"SAJOU_DEV_SERVER": "http://localhost:5175"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### Development (from this repo)
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"sajou": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["tsx", "packages/mcp-server/src/index.ts"],
|
|
43
|
+
"env": {
|
|
44
|
+
"SAJOU_DEV_SERVER": "http://localhost:5175"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Tools
|
|
52
|
+
|
|
53
|
+
| Tool | Description |
|
|
54
|
+
|------|-------------|
|
|
55
|
+
| `emit_signal` | Emit a signal to the scene. Triggers choreographies. |
|
|
56
|
+
| `get_scene_state` | Get current scene entities (id, position, visibility). |
|
|
57
|
+
| `get_choreographies` | List available choreographies with descriptions. |
|
|
58
|
+
| `list_themes` | List available themes (citadel, office). |
|
|
59
|
+
| `get_catalog` | Get entity catalog for a theme (buildings, units, etc.). |
|
|
60
|
+
| `map_signals` | Map a signal type to a choreography. |
|
|
61
|
+
|
|
62
|
+
## Environment variables
|
|
63
|
+
|
|
64
|
+
| Variable | Default | Description |
|
|
65
|
+
|----------|---------|-------------|
|
|
66
|
+
| `SAJOU_DEV_SERVER` | `http://localhost:5175` | URL of the scene-builder dev server |
|
|
67
|
+
|
|
68
|
+
## Architecture
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
AI Agent (Claude) ──MCP/stdio──> sajou-mcp ──HTTP──> scene-builder dev server
|
|
72
|
+
│
|
|
73
|
+
├── POST /api/signal (emit signals)
|
|
74
|
+
├── GET /api/scene/state (scene state)
|
|
75
|
+
└── GET /__signals__/stream (SSE)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The MCP server is a thin adapter. It translates MCP tool calls into HTTP requests against the scene-builder's existing API endpoints. The scene-builder handles all signal routing, choreography execution, and rendering.
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express application — mounts all route modules.
|
|
3
|
+
*
|
|
4
|
+
* This is the HTTP server for the sajou state server. It provides:
|
|
5
|
+
* - REST API for scene state (read/write)
|
|
6
|
+
* - SSE streams for real-time updates
|
|
7
|
+
* - Signal ingestion endpoint
|
|
8
|
+
* - Local service discovery
|
|
9
|
+
* - CORS proxy for browser clients
|
|
10
|
+
* - Tap hook management
|
|
11
|
+
*
|
|
12
|
+
* Used both as a standalone server (--http flag) and as Express middleware
|
|
13
|
+
* for the botoul deployment (createMcpRouter export).
|
|
14
|
+
*/
|
|
15
|
+
import express from "express";
|
|
16
|
+
/** Create the Express app with all routes mounted. */
|
|
17
|
+
export declare function createApp(): express.Express;
|
|
18
|
+
/**
|
|
19
|
+
* Create an Express Router for use as middleware (botoul deployment).
|
|
20
|
+
*
|
|
21
|
+
* Mounts REST routes + MCP Streamable HTTP transport on the router root.
|
|
22
|
+
* When the parent app mounts this on `/mcp`, MCP requests go to `/mcp/`
|
|
23
|
+
* and REST API to `/mcp/api/*`.
|
|
24
|
+
*
|
|
25
|
+
* Usage in botoul's index.js:
|
|
26
|
+
* ```js
|
|
27
|
+
* const { createMcpRouter } = require('./lib/sajou-server.cjs');
|
|
28
|
+
* app.use('/mcp', createMcpRouter());
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function createMcpRouter(): express.Router;
|
|
32
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAwB9B,sDAAsD;AACtD,wBAAgB,SAAS,IAAI,OAAO,CAAC,OAAO,CAe3C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,MAAM,CAiBhD"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express application — mounts all route modules.
|
|
3
|
+
*
|
|
4
|
+
* This is the HTTP server for the sajou state server. It provides:
|
|
5
|
+
* - REST API for scene state (read/write)
|
|
6
|
+
* - SSE streams for real-time updates
|
|
7
|
+
* - Signal ingestion endpoint
|
|
8
|
+
* - Local service discovery
|
|
9
|
+
* - CORS proxy for browser clients
|
|
10
|
+
* - Tap hook management
|
|
11
|
+
*
|
|
12
|
+
* Used both as a standalone server (--http flag) and as Express middleware
|
|
13
|
+
* for the botoul deployment (createMcpRouter export).
|
|
14
|
+
*/
|
|
15
|
+
import express from "express";
|
|
16
|
+
import { createSceneRoutes } from "./routes/scene.js";
|
|
17
|
+
import { createSignalRoutes } from "./routes/signals.js";
|
|
18
|
+
import { createDiscoveryRoutes } from "./routes/discovery.js";
|
|
19
|
+
import { createTapRoutes } from "./routes/tap.js";
|
|
20
|
+
import { createProxyRoutes } from "./routes/proxy.js";
|
|
21
|
+
import { mountMcpTransport } from "./mcp/transport.js";
|
|
22
|
+
import { createServer } from "./server.js";
|
|
23
|
+
/** Simple CORS middleware — allows all origins (dev-friendly). */
|
|
24
|
+
function corsMiddleware(_req, res, next) {
|
|
25
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
26
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
27
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
28
|
+
res.setHeader("Access-Control-Max-Age", "86400");
|
|
29
|
+
if (_req.method === "OPTIONS") {
|
|
30
|
+
res.status(204).end();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
next();
|
|
34
|
+
}
|
|
35
|
+
/** Create the Express app with all routes mounted. */
|
|
36
|
+
export function createApp() {
|
|
37
|
+
const app = express();
|
|
38
|
+
// Global middleware
|
|
39
|
+
app.use(corsMiddleware);
|
|
40
|
+
app.use(express.json({ limit: "10mb" }));
|
|
41
|
+
// Mount all route modules
|
|
42
|
+
app.use(createSceneRoutes());
|
|
43
|
+
app.use(createSignalRoutes());
|
|
44
|
+
app.use(createDiscoveryRoutes());
|
|
45
|
+
app.use(createTapRoutes());
|
|
46
|
+
app.use(createProxyRoutes());
|
|
47
|
+
return app;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create an Express Router for use as middleware (botoul deployment).
|
|
51
|
+
*
|
|
52
|
+
* Mounts REST routes + MCP Streamable HTTP transport on the router root.
|
|
53
|
+
* When the parent app mounts this on `/mcp`, MCP requests go to `/mcp/`
|
|
54
|
+
* and REST API to `/mcp/api/*`.
|
|
55
|
+
*
|
|
56
|
+
* Usage in botoul's index.js:
|
|
57
|
+
* ```js
|
|
58
|
+
* const { createMcpRouter } = require('./lib/sajou-server.cjs');
|
|
59
|
+
* app.use('/mcp', createMcpRouter());
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function createMcpRouter() {
|
|
63
|
+
const router = express.Router();
|
|
64
|
+
router.use(corsMiddleware);
|
|
65
|
+
router.use(express.json({ limit: "10mb" }));
|
|
66
|
+
// REST API routes
|
|
67
|
+
router.use(createSceneRoutes());
|
|
68
|
+
router.use(createSignalRoutes());
|
|
69
|
+
router.use(createDiscoveryRoutes());
|
|
70
|
+
router.use(createTapRoutes());
|
|
71
|
+
router.use(createProxyRoutes());
|
|
72
|
+
// MCP Streamable HTTP — mounted on "/" (router is already prefixed by parent)
|
|
73
|
+
mountMcpTransport(router, createServer, "/");
|
|
74
|
+
return router;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,kEAAkE;AAClE,SAAS,cAAc,CAAC,IAAa,EAAE,GAAa,EAAE,IAAkB;IACtE,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,iCAAiC,CAAC,CAAC;IACjF,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,6BAA6B,CAAC,CAAC;IAC7E,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;IAEjD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IACD,IAAI,EAAE,CAAC;AACT,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IAEtB,oBAAoB;IACpB,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAEzC,0BAA0B;IAC1B,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC7B,GAAG,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC9B,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACjC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;IAC3B,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAE7B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAE5C,kBAAkB;IAClB,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAEhC,8EAA8E;IAC9E,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IAE7C,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sajou server entry point — supports both stdio (MCP) and HTTP modes.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node dist/index.js → stdio transport (default, for Claude Code)
|
|
7
|
+
* node dist/index.js --http → HTTP server on port 3000
|
|
8
|
+
* node dist/index.js --http 8080 → HTTP server on custom port
|
|
9
|
+
*
|
|
10
|
+
* In HTTP mode, the server provides:
|
|
11
|
+
* - REST API on /api/* (same endpoints as the old Vite dev server)
|
|
12
|
+
* - MCP Streamable HTTP on /mcp
|
|
13
|
+
* - SSE streams for real-time updates
|
|
14
|
+
*
|
|
15
|
+
* In stdio mode, the server behaves exactly as before — MCP JSON-RPC over stdin/stdout.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sajou server entry point — supports both stdio (MCP) and HTTP modes.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node dist/index.js → stdio transport (default, for Claude Code)
|
|
7
|
+
* node dist/index.js --http → HTTP server on port 3000
|
|
8
|
+
* node dist/index.js --http 8080 → HTTP server on custom port
|
|
9
|
+
*
|
|
10
|
+
* In HTTP mode, the server provides:
|
|
11
|
+
* - REST API on /api/* (same endpoints as the old Vite dev server)
|
|
12
|
+
* - MCP Streamable HTTP on /mcp
|
|
13
|
+
* - SSE streams for real-time updates
|
|
14
|
+
*
|
|
15
|
+
* In stdio mode, the server behaves exactly as before — MCP JSON-RPC over stdin/stdout.
|
|
16
|
+
*/
|
|
17
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
|
+
import { createServer } from "./server.js";
|
|
19
|
+
import { createApp } from "./app.js";
|
|
20
|
+
import { mountMcpTransport } from "./mcp/transport.js";
|
|
21
|
+
import { cleanupTapHooks } from "./routes/tap.js";
|
|
22
|
+
/** Parse --http flag and optional port from argv. */
|
|
23
|
+
function parseArgs() {
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
const httpIndex = args.indexOf("--http");
|
|
26
|
+
if (httpIndex === -1) {
|
|
27
|
+
return { mode: "stdio", port: 3000 };
|
|
28
|
+
}
|
|
29
|
+
const portArg = args[httpIndex + 1];
|
|
30
|
+
const port = portArg && !portArg.startsWith("-") ? parseInt(portArg, 10) : 3000;
|
|
31
|
+
return { mode: "http", port: isNaN(port) ? 3000 : port };
|
|
32
|
+
}
|
|
33
|
+
async function main() {
|
|
34
|
+
const { mode, port } = parseArgs();
|
|
35
|
+
if (mode === "stdio") {
|
|
36
|
+
// Classic stdio mode — MCP JSON-RPC over stdin/stdout
|
|
37
|
+
const server = createServer();
|
|
38
|
+
const transport = new StdioServerTransport();
|
|
39
|
+
await server.connect(transport);
|
|
40
|
+
process.stderr.write("[sajou] Server started (stdio mode)\n");
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// HTTP mode — Express server with REST + MCP Streamable HTTP
|
|
44
|
+
const app = createApp();
|
|
45
|
+
mountMcpTransport(app, createServer);
|
|
46
|
+
const httpServer = app.listen(port, () => {
|
|
47
|
+
process.stderr.write(`[sajou] Server started on http://localhost:${port}\n`);
|
|
48
|
+
process.stderr.write(`[sajou] REST API: http://localhost:${port}/api/*\n`);
|
|
49
|
+
process.stderr.write(`[sajou] MCP: http://localhost:${port}/mcp\n`);
|
|
50
|
+
});
|
|
51
|
+
// Cleanup on shutdown
|
|
52
|
+
const shutdown = () => {
|
|
53
|
+
cleanupTapHooks();
|
|
54
|
+
httpServer.close();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
};
|
|
57
|
+
process.on("SIGINT", shutdown);
|
|
58
|
+
process.on("SIGTERM", shutdown);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
main().catch((error) => {
|
|
62
|
+
process.stderr.write(`[sajou] Fatal error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Streamable HTTP transport — mounts on /mcp for remote agents.
|
|
3
|
+
*
|
|
4
|
+
* Creates a per-session StreamableHTTPServerTransport and wires it
|
|
5
|
+
* to the same McpServer instance used by stdio. Coexists with the
|
|
6
|
+
* REST routes on the same Express app.
|
|
7
|
+
*/
|
|
8
|
+
import type { Router } from "express";
|
|
9
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
/**
|
|
11
|
+
* Mount the MCP Streamable HTTP endpoint on an Express router.
|
|
12
|
+
*
|
|
13
|
+
* @param router - Express Router (or app, which extends Router) to mount on
|
|
14
|
+
* @param serverFactory - Factory that creates McpServer instances (one per session)
|
|
15
|
+
* @param path - Route path to mount on (default "/mcp")
|
|
16
|
+
*/
|
|
17
|
+
export declare function mountMcpTransport(router: Router, serverFactory: () => McpServer, path?: string): void;
|
|
18
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/mcp/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAqB,MAAM,EAAE,MAAM,SAAS,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA8CzE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,EAAE,IAAI,SAAS,GAAG,IAAI,CAIrG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Streamable HTTP transport — mounts on /mcp for remote agents.
|
|
3
|
+
*
|
|
4
|
+
* Creates a per-session StreamableHTTPServerTransport and wires it
|
|
5
|
+
* to the same McpServer instance used by stdio. Coexists with the
|
|
6
|
+
* REST routes on the same Express app.
|
|
7
|
+
*/
|
|
8
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
9
|
+
/** Active transports keyed by session ID. */
|
|
10
|
+
const transports = new Map();
|
|
11
|
+
/** Handler for MCP Streamable HTTP requests. */
|
|
12
|
+
async function mcpHandler(req, res, serverFactory) {
|
|
13
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
14
|
+
if (sessionId && transports.has(sessionId)) {
|
|
15
|
+
const transport = transports.get(sessionId);
|
|
16
|
+
await transport.handleRequest(req, res, req.body);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (req.method === "POST") {
|
|
20
|
+
const transport = new StreamableHTTPServerTransport({
|
|
21
|
+
sessionIdGenerator: () => crypto.randomUUID(),
|
|
22
|
+
});
|
|
23
|
+
transport.onclose = () => {
|
|
24
|
+
if (transport.sessionId) {
|
|
25
|
+
transports.delete(transport.sessionId);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const server = serverFactory();
|
|
29
|
+
await server.connect(transport);
|
|
30
|
+
// handleRequest processes the initialize message and assigns sessionId
|
|
31
|
+
await transport.handleRequest(req, res, req.body);
|
|
32
|
+
// Store AFTER handleRequest so the sessionId is available
|
|
33
|
+
if (transport.sessionId) {
|
|
34
|
+
transports.set(transport.sessionId, transport);
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
res.status(400).json({
|
|
39
|
+
jsonrpc: "2.0",
|
|
40
|
+
error: { code: -32000, message: "No valid session. Send a POST to initialize." },
|
|
41
|
+
id: null,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Mount the MCP Streamable HTTP endpoint on an Express router.
|
|
46
|
+
*
|
|
47
|
+
* @param router - Express Router (or app, which extends Router) to mount on
|
|
48
|
+
* @param serverFactory - Factory that creates McpServer instances (one per session)
|
|
49
|
+
* @param path - Route path to mount on (default "/mcp")
|
|
50
|
+
*/
|
|
51
|
+
export function mountMcpTransport(router, serverFactory, path = "/mcp") {
|
|
52
|
+
router.all(path, (req, res) => {
|
|
53
|
+
void mcpHandler(req, res, serverFactory);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/mcp/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAGnG,6CAA6C;AAC7C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAyC,CAAC;AAEpE,gDAAgD;AAChD,KAAK,UAAU,UAAU,CAAC,GAAY,EAAE,GAAa,EAAE,aAA8B;IACnF,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAuB,CAAC;IAEtE,IAAI,SAAS,IAAI,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3C,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;QAC7C,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;YAClD,kBAAkB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE;SAC9C,CAAC,CAAC;QAEH,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE;YACvB,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;gBACxB,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACzC,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEhC,uEAAuE;QACvE,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAElD,0DAA0D;QAC1D,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;YACxB,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACjD,CAAC;QACD,OAAO;IACT,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACnB,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,8CAA8C,EAAE;QAChF,EAAE,EAAE,IAAI;KACT,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,aAA8B,EAAE,IAAI,GAAG,MAAM;IAC7F,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QAC/C,KAAK,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware entry point — for CJS bundle used by botoul.
|
|
3
|
+
*
|
|
4
|
+
* Exports `createMcpRouter()` without starting any server.
|
|
5
|
+
* This is the entry point for esbuild when building the botoul bundle.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```js
|
|
9
|
+
* const { createMcpRouter } = require('./lib/sajou-mcp.cjs');
|
|
10
|
+
* app.use('/mcp', createMcpRouter());
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export { createMcpRouter } from "./app.js";
|
|
14
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware entry point — for CJS bundle used by botoul.
|
|
3
|
+
*
|
|
4
|
+
* Exports `createMcpRouter()` without starting any server.
|
|
5
|
+
* This is the entry point for esbuild when building the botoul bundle.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```js
|
|
9
|
+
* const { createMcpRouter } = require('./lib/sajou-mcp.cjs');
|
|
10
|
+
* app.use('/mcp', createMcpRouter());
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export { createMcpRouter } from "./app.js";
|
|
14
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local service discovery + OpenClaw token routes.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from localDiscoveryPlugin and openclawTokenPlugin in vite.config.ts.
|
|
5
|
+
*/
|
|
6
|
+
import { Router } from "express";
|
|
7
|
+
export declare function createDiscoveryRoutes(): Router;
|
|
8
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/routes/discovery.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AA+FjC,wBAAgB,qBAAqB,IAAI,MAAM,CA0F9C"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local service discovery + OpenClaw token routes.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from localDiscoveryPlugin and openclawTokenPlugin in vite.config.ts.
|
|
5
|
+
*/
|
|
6
|
+
import { Router } from "express";
|
|
7
|
+
import { createConnection } from "node:net";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Probe helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/** Probe a TCP port on localhost. Resolves true if something is listening. */
|
|
14
|
+
function tcpProbe(port, timeoutMs = 300) {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
const socket = createConnection({ host: "127.0.0.1", port });
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
socket.destroy();
|
|
19
|
+
resolve(false);
|
|
20
|
+
}, timeoutMs);
|
|
21
|
+
socket.on("connect", () => {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
socket.destroy();
|
|
24
|
+
resolve(true);
|
|
25
|
+
});
|
|
26
|
+
socket.on("error", () => {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
socket.destroy();
|
|
29
|
+
resolve(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** Probe an HTTP endpoint. Returns models list on success. */
|
|
34
|
+
async function httpProbe(url, timeoutMs = 300) {
|
|
35
|
+
try {
|
|
36
|
+
const resp = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
37
|
+
if (!resp.ok)
|
|
38
|
+
return { ok: false };
|
|
39
|
+
const json = (await resp.json());
|
|
40
|
+
const data = json["data"];
|
|
41
|
+
if (Array.isArray(data)) {
|
|
42
|
+
const models = data.map((m) => {
|
|
43
|
+
const entry = m;
|
|
44
|
+
return String(entry["id"] ?? "unknown");
|
|
45
|
+
});
|
|
46
|
+
return { ok: true, models };
|
|
47
|
+
}
|
|
48
|
+
return { ok: true, models: [] };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return { ok: false };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// OpenClaw token reader
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
const OPENCLAW_CONFIG_PATH = join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? ".", ".openclaw", "openclaw.json");
|
|
58
|
+
async function readOpenClawToken() {
|
|
59
|
+
try {
|
|
60
|
+
const raw = await readFile(OPENCLAW_CONFIG_PATH, "utf8");
|
|
61
|
+
const config = JSON.parse(raw);
|
|
62
|
+
return config.gateway?.auth?.token ?? null;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Routes
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
export function createDiscoveryRoutes() {
|
|
72
|
+
const router = Router();
|
|
73
|
+
// GET /api/discover/local — probe known local services
|
|
74
|
+
router.get("/api/discover/local", (_req, res) => {
|
|
75
|
+
Promise.allSettled([
|
|
76
|
+
// Claude Code — always available (SSE internal endpoint)
|
|
77
|
+
Promise.resolve({
|
|
78
|
+
id: "local:claude-code",
|
|
79
|
+
label: "Claude Code",
|
|
80
|
+
protocol: "sse",
|
|
81
|
+
url: "/__signals__/stream",
|
|
82
|
+
available: true,
|
|
83
|
+
models: [],
|
|
84
|
+
}),
|
|
85
|
+
// OpenClaw — TCP probe on 18789
|
|
86
|
+
tcpProbe(18789, 300).then((up) => ({
|
|
87
|
+
id: "local:openclaw",
|
|
88
|
+
label: "OpenClaw",
|
|
89
|
+
protocol: "openclaw",
|
|
90
|
+
url: "ws://127.0.0.1:18789",
|
|
91
|
+
available: up,
|
|
92
|
+
needsApiKey: true,
|
|
93
|
+
models: [],
|
|
94
|
+
})),
|
|
95
|
+
// LM Studio — HTTP probe on 1234
|
|
96
|
+
httpProbe("http://127.0.0.1:1234/v1/models", 300).then((r) => ({
|
|
97
|
+
id: "local:lm-studio",
|
|
98
|
+
label: "LM Studio",
|
|
99
|
+
protocol: "openai",
|
|
100
|
+
url: "http://127.0.0.1:1234",
|
|
101
|
+
available: r.ok,
|
|
102
|
+
needsApiKey: true,
|
|
103
|
+
models: r.models ?? [],
|
|
104
|
+
})),
|
|
105
|
+
// Ollama — HTTP probe on 11434
|
|
106
|
+
httpProbe("http://127.0.0.1:11434/v1/models", 300).then((r) => ({
|
|
107
|
+
id: "local:ollama",
|
|
108
|
+
label: "Ollama",
|
|
109
|
+
protocol: "openai",
|
|
110
|
+
url: "http://127.0.0.1:11434",
|
|
111
|
+
available: r.ok,
|
|
112
|
+
models: r.models ?? [],
|
|
113
|
+
})),
|
|
114
|
+
// Codex — TCP probe on 4500
|
|
115
|
+
tcpProbe(4500, 300).then((up) => ({
|
|
116
|
+
id: "local:codex",
|
|
117
|
+
label: "Codex",
|
|
118
|
+
protocol: "codex",
|
|
119
|
+
url: "ws://127.0.0.1:4500",
|
|
120
|
+
available: up,
|
|
121
|
+
models: [],
|
|
122
|
+
})),
|
|
123
|
+
]).then((results) => {
|
|
124
|
+
const services = [];
|
|
125
|
+
for (const result of results) {
|
|
126
|
+
if (result.status === "fulfilled") {
|
|
127
|
+
services.push(result.value);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
res.json({ services });
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
// GET /api/openclaw/token — serve OpenClaw gateway token (dev only)
|
|
134
|
+
router.get("/api/openclaw/token", (req, res) => {
|
|
135
|
+
const origin = req.headers["origin"] ?? "";
|
|
136
|
+
// In standalone server mode, allow any localhost origin
|
|
137
|
+
if (origin && !origin.includes("localhost") && !origin.includes("127.0.0.1") && !origin.includes("0.0.0.0")) {
|
|
138
|
+
res.status(403).json({ ok: false, error: "Forbidden origin" });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
readOpenClawToken().then((token) => {
|
|
142
|
+
if (origin) {
|
|
143
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
144
|
+
}
|
|
145
|
+
if (token) {
|
|
146
|
+
res.json({ ok: true, token });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
res.json({ ok: false });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
return router;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/routes/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS proxy route — forwards requests to external services.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from corsProxyPlugin in vite.config.ts.
|
|
5
|
+
* Used by the browser to bypass CORS restrictions when probing
|
|
6
|
+
* LM Studio, Ollama, and other local services.
|
|
7
|
+
*/
|
|
8
|
+
import { Router } from "express";
|
|
9
|
+
export declare function createProxyRoutes(): Router;
|
|
10
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/routes/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,wBAAgB,iBAAiB,IAAI,MAAM,CA+E1C"}
|