create-caspian-app 0.2.0-beta.34 → 0.2.0-beta.36
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.
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
- Use this source-of-truth order: app runtime and app-owned code first, installed `casp` runtime second, packaged markdown docs third.
|
|
9
9
|
- Read `./caspian.config.json` almost immediately before making feature, tooling, scaffolding, or file-placement decisions. Treat it as the workspace feature gate for flags such as `backendOnly`, `tailwindcss`, `mcp`, `prisma`, `typescript`, and `componentScanDirs`.
|
|
10
|
+
- Treat `caspian.config.json` as the single source of truth for whether optional Caspian features are enabled in the current workspace. Use feature-specific docs, files, and commands only after the matching flag is confirmed as enabled.
|
|
11
|
+
- If a feature is disabled and the user wants it, ask whether they want to enable it first, then update `caspian.config.json` and follow `npx casp update project` so framework-managed files align with the new feature set.
|
|
10
12
|
- For current repo behavior, trust `main.py`, `src/lib/**`, `public/js/**`, `prisma/**`, and `src/app/**` over generic Caspian docs.
|
|
11
13
|
- For framework internals, trust `.venv/Lib/site-packages/casp/**` over generic or older upstream guidance.
|
|
12
14
|
- When docs and runtime disagree, align the docs to the code that actually runs in this workspace.
|
|
13
15
|
- When `prisma/schema.prisma` changes, follow this order: run `npx prisma migrate dev`; if the change affects seed flow or `prisma/seed.ts`, run `npx prisma generate` and then `npx prisma db seed`; then run `npx ppy generate` so the Python ORM stays aligned with the schema.
|
|
14
16
|
- Reuse the existing Python database layer in `src/lib/prisma/**`; do not create a second app-owned database abstraction unless the user explicitly asks for one.
|
|
15
17
|
- Treat `src/lib/prisma/__init__.py`, `src/lib/prisma/db.py`, `src/lib/prisma/models.py`, and `settings/prisma-schema.json` as generated outputs owned by `npx ppy generate`; do not create or hand-edit them manually.
|
|
18
|
+
- Treat `package.json` scripts as opt-in operations. Do not run `npm run dev`, `npm run build`, or other npm scripts unless the user explicitly asks, the task genuinely requires that exact script, or deployment preparation needs `npm run build`.
|
|
19
|
+
- Use `npm run build` for deployment prep or an explicit build request, not as the default validation step for routine route, feature, or documentation edits.
|
|
20
|
+
- Let the running dev stack own generated outputs such as `public/css/styles.css`, `settings/component-map.json`, `settings/files-list.json`, `__pycache__/`, and `.pyc` files. Treat those as generated artifacts rather than authored source.
|
|
21
|
+
- Never treat `__pycache__/` directories or `.pyc` files as files to edit, regenerate on purpose, or keep in the final diff.
|
|
22
|
+
- Treat `settings/component-map.json` and `settings/files-list.json` as generated outputs owned by `settings/component-map.ts` and `settings/files-list.ts`; inspect them when needed, but do not hand-edit them.
|
|
16
23
|
- When `caspian.config.json` has `mcp: true`, treat `src/lib/mcp/mcp_server.py` as the app-owned FastMCP server and `src/lib/mcp/fastmcp.json` as the default MCP config. Use `npm run mcp` or `fastmcp run src/lib/mcp/fastmcp.json`; do not assume root `fastmcp.json` auto-discovery.
|
|
17
24
|
- Keep auth policy in `src/lib/auth/auth_config.py` and keep auth bootstrap, middleware wiring, and provider registration in `main.py`.
|
|
18
25
|
- In app-owned starter config like this workspace, routes start public because `src/lib/auth/auth_config.py` sets `is_all_routes_private=False` by default.
|
|
@@ -41,7 +48,7 @@
|
|
|
41
48
|
|
|
42
49
|
- Keep `src/lib/` for app-owned shared code, service wrappers, and reusable helpers.
|
|
43
50
|
- Reuse the generated `src/lib/prisma/` package for Python database access, but do not hand-edit files under `src/lib/prisma/`; regenerate them with `npx ppy generate` after schema changes.
|
|
44
|
-
-
|
|
51
|
+
- When `caspian.config.json` has `mcp: true`, keep app-owned MCP tools in `src/lib/mcp/mcp_server.py` and keep the default FastMCP config in `src/lib/mcp/fastmcp.json`. If those locations change, update `settings/restart-mcp.ts` and the MCP docs together.
|
|
45
52
|
- Keep auth policy in `src/lib/auth/auth_config.py`. Keep auth bootstrap and middleware order changes in `main.py`.
|
|
46
53
|
|
|
47
54
|
### `public/js/main.js`
|
|
@@ -90,10 +97,14 @@
|
|
|
90
97
|
3. the markdown file being edited
|
|
91
98
|
- Keep repo-specific facts accurate when they matter:
|
|
92
99
|
- `caspian.config.json` is the first config file to read for enabled workspace features and scan directories
|
|
100
|
+
- `caspian.config.json` is the single source of truth for whether an optional feature is enabled in the current workspace
|
|
93
101
|
- this workspace already has `src/lib/prisma/**`
|
|
94
|
-
- this workspace
|
|
95
|
-
-
|
|
96
|
-
-
|
|
102
|
+
- this workspace currently has `mcp: false` in `caspian.config.json`, so do not assume `src/lib/mcp/**`, `settings/restart-mcp.ts`, or `npm run mcp` exist until MCP is explicitly enabled
|
|
103
|
+
- `package.json` currently defines `projectName`, `tailwind`, `tailwind:build`, `browserSync`, `browserSync:build`, `dev`, and `build`
|
|
104
|
+
- if a feature flag is false and the user wants that feature, ask before enabling it, then update `caspian.config.json` and use `npx casp update project` to refresh framework-managed files
|
|
105
|
+
- do not run `package.json` scripts by default for ordinary source edits; only use them when the user explicitly asks, the task requires them, or deployment prep needs `npm run build`
|
|
106
|
+
- `npm run dev` is a long-running local stack command that can regenerate `public/css/styles.css`, `settings/component-map.json`, `settings/files-list.json`, `__pycache__/`, and `.pyc` artifacts; those are generated outputs, not authored source files
|
|
107
|
+
- `settings/component-map.ts` and `settings/files-list.ts` own `settings/component-map.json` and `settings/files-list.json`; inspect the JSON when needed, but let the framework regenerate it instead of editing it by hand
|
|
97
108
|
- auth policy lives in `src/lib/auth/auth_config.py`
|
|
98
109
|
- the app-owned starter config in this workspace begins public-first with `is_all_routes_private=False`, so treat routes as public by default unless the app explicitly switches to all-private mode
|
|
99
110
|
- choose all-private mode in `src/lib/auth/auth_config.py` only when public routes are the minority; otherwise keep mixed mode and maintain `private_routes`
|
package/dist/AGENTS.md
CHANGED
|
@@ -30,6 +30,8 @@ If docs differ from either of those, update the docs to match the code that actu
|
|
|
30
30
|
|
|
31
31
|
Before making feature, tooling, or scaffolding decisions, read `caspian.config.json` almost immediately. Treat it as the workspace feature gate for flags such as `backendOnly`, `tailwindcss`, `mcp`, `prisma`, `typescript`, and `componentScanDirs`.
|
|
32
32
|
|
|
33
|
+
Treat `caspian.config.json` as the single source of truth for whether an optional Caspian feature is enabled in the current workspace. Use feature-specific docs only after the matching flag is confirmed as enabled. If a feature is disabled and the user wants it, ask whether they want to enable it first, then follow the Caspian update workflow to refresh framework-managed files.
|
|
34
|
+
|
|
33
35
|
## Verified Workspace Facts
|
|
34
36
|
|
|
35
37
|
- Local Caspian docs live under `node_modules/caspian-utils/dist/docs/`.
|
|
@@ -45,17 +47,19 @@ Before making feature, tooling, or scaffolding decisions, read `caspian.config.j
|
|
|
45
47
|
- Prefer logout via auth-protected RPC from page-level or component-level UI: `pp.rpc("signout")` backed by `@rpc(require_auth=True)`. Use a dedicated signout route only for plain form POST or no-JavaScript edge cases.
|
|
46
48
|
- Protect customized `src/lib/auth/auth_config.py` from framework updates by adding `./src/lib/auth/auth_config.py` to `excludeFiles` in `caspian.config.json`.
|
|
47
49
|
- This workspace already has an app-owned Python database layer in `src/lib/prisma/`.
|
|
48
|
-
- This workspace
|
|
49
|
-
- `package.json`
|
|
50
|
+
- This workspace currently has `mcp: false` in `caspian.config.json`, so do not assume `src/lib/mcp/**`, `settings/restart-mcp.ts`, or `npm run mcp` exist unless MCP is explicitly enabled later.
|
|
51
|
+
- `package.json` currently defines `projectName`, `tailwind`, `tailwind:build`, `browserSync`, `browserSync:build`, `dev`, and `build`. Treat those scripts as opt-in operational commands, not default validation steps for ordinary source edits.
|
|
50
52
|
- Reuse `src/lib/prisma/prisma`, `PrismaClient`, generated models, and helper types instead of creating a second Python database abstraction.
|
|
51
53
|
- Prisma schema source of truth is `prisma/schema.prisma`.
|
|
52
54
|
- The schema-change workflow in this workspace is: `npx prisma migrate dev`; if seed flow or `prisma/seed.ts` is involved, run `npx prisma generate` and then `npx prisma db seed`; then run `npx ppy generate`.
|
|
53
55
|
- `npx ppy generate` owns `src/lib/prisma/__init__.py`, `src/lib/prisma/db.py`, `src/lib/prisma/models.py`, and `settings/prisma-schema.json`; do not hand-edit those generated files.
|
|
54
|
-
- `caspian.config.json` is the first config file to check for enabled workspace features. In the current workspace it sets `backendOnly: false`, `tailwindcss: true`, `mcp:
|
|
56
|
+
- `caspian.config.json` is the first config file to check for enabled workspace features. In the current workspace it sets `backendOnly: false`, `tailwindcss: true`, `mcp: false`, `prisma: true`, `typescript: false`, and `componentScanDirs: ["src"]`.
|
|
55
57
|
- PulsePoint runtime code is shipped in `public/js/pp-reactive-v2.js` and loaded from `public/js/main.js`.
|
|
56
58
|
- `pp-component` is injected by the Python render pipeline onto page, layout, and component roots; authored route and component templates should not add it manually.
|
|
57
59
|
- `main.py` runs `transform_scripts(...)`, so authored body `<script>` tags are rewritten to `<script type="text/pp">` in rendered HTML; route, layout, and component templates should write plain `<script>` in source.
|
|
58
60
|
- Route and component HTML templates must keep exactly one top-level lowercase HTML element so Caspian can inject `pp-component`. Think React-style single parent wrapper: good `<div>...</div>` with any owned script inside that same root, bad sibling top-level tags such as `<div>...</div><script ...></script>`.
|
|
61
|
+
- When `npm run dev` is intentionally running, let that long-running stack own generated outputs such as `public/css/styles.css`, `settings/component-map.json`, `settings/files-list.json`, `__pycache__/`, and `.pyc` files. Treat those as generated artifacts, not authored source.
|
|
62
|
+
- `settings/component-map.json` and `settings/files-list.json` are generated by `settings/component-map.ts` and `settings/files-list.ts` through the dev and build pipelines. Analyze them when needed, but do not hand-edit them.
|
|
59
63
|
- In the current router inside `main.py`, path params are passed to `page()` as the first positional `dict` argument.
|
|
60
64
|
- Matching query params can still be injected by name, and `request` is injected by keyword when declared.
|
|
61
65
|
- The installed `casp.layout` runtime calls `layout()` synchronously. Keep async I/O in `page()` or `@rpc()`.
|
|
@@ -86,6 +90,12 @@ Use this map before making changes.
|
|
|
86
90
|
- Keep app-owned shared code in `src/lib/**`.
|
|
87
91
|
- Keep route-specific logic in `src/app/**`.
|
|
88
92
|
- Read `caspian.config.json` before deciding whether a Caspian feature should be used, documented, scaffolded, or avoided in the current workspace.
|
|
93
|
+
- Treat `caspian.config.json` as the single source of truth for optional features. Do not use feature-specific files, commands, or docs until the corresponding flag is enabled.
|
|
94
|
+
- If a feature flag is false and the user wants that feature, ask for confirmation first, then update `caspian.config.json` and run `npx casp update project` so framework-managed files align with the new feature set.
|
|
95
|
+
- Do not run `package.json` scripts unless the user explicitly asks for them, the task genuinely requires that exact script, or deployment preparation needs `npm run build`.
|
|
96
|
+
- Use `npm run build` for deployment prep or an explicit build request, not as the default validation step for routine route, feature, or documentation edits.
|
|
97
|
+
- Never treat `__pycache__/` directories or `.pyc` files as authored source. Do not edit them manually and do not leave them in the final diff if a tool run creates them.
|
|
98
|
+
- Never hand-edit `settings/component-map.json` or `settings/files-list.json`. Inspect them if needed, but let the framework regeneration flow update them.
|
|
89
99
|
- Treat `.venv/Lib/site-packages/casp/**` as framework internals unless the task is explicitly about Caspian core behavior or installed-runtime documentation.
|
|
90
100
|
- When a task involves Python-side database access, reuse `src/lib/prisma/**` instead of introducing a parallel helper, but do not hand-edit generated files in that directory.
|
|
91
101
|
- When `prisma/schema.prisma` changes, run `npx prisma migrate dev` first. If seed flow or `prisma/seed.ts` is involved, run `npx prisma generate` and then `npx prisma db seed`. After that, run `npx ppy generate` so the Python ORM layer and `settings/prisma-schema.json` stay aligned.
|
|
@@ -111,9 +121,11 @@ Use this map before making changes.
|
|
|
111
121
|
The packaged docs in this workspace are already mostly aligned with the installed runtime, but keep these repo-specific clarifications in mind:
|
|
112
122
|
|
|
113
123
|
- `database.md` is the source doc for the Prisma and Python ORM workflow in this repo: schema changes go through `npx prisma migrate dev`, optional `npx prisma generate` plus `npx prisma db seed`, then `npx ppy generate`, and generated ORM files under `src/lib/prisma/` plus `settings/prisma-schema.json` are not hand-edited.
|
|
114
|
-
- `mcp.md` is the source doc for
|
|
124
|
+
- `mcp.md` is the source doc for MCP-enabled workspaces, but in this workspace `caspian.config.json` currently has `mcp: false`, so do not assume `src/lib/mcp/**` or `npm run mcp` exist until MCP is explicitly enabled.
|
|
125
|
+
- Feature-specific docs are conditional on `caspian.config.json`: use `database.md` only when `prisma: true`, use `mcp.md` only when `mcp: true`, and treat disabled-feature docs as reference material until the user chooses to enable that feature.
|
|
115
126
|
- `auth.md` is the source doc for auth routing guidance: choose all-private mode only when public routes are the minority, keep auth policy in `src/lib/auth/auth_config.py`, protect that file with `excludeFiles` if customized, prefer auth-protected RPC logout from page or component UI, and keep dedicated signout routes for form-post or no-JavaScript fallbacks.
|
|
116
127
|
- `state.md` is correct to warn that cross-request persistence depends on `request.state.session`, which is not bridged in the current `main.py`.
|
|
128
|
+
- `commands.md`, `installation.md`, and `project-structure.md` are the source docs for this repo's package-script guardrails: do not auto-run npm scripts for ordinary edits, use `npm run build` only for deployment prep or explicit build requests, and treat `settings/component-map.json`, `settings/files-list.json`, `__pycache__/`, and `.pyc` files as generated artifacts.
|
|
117
129
|
- `routing.md`, `components.md`, `auth.md`, `fetch-data.md`, `cache.md`, `pulsepoint.md`, and `validation.md` should continue to be validated against the installed `casp` package before any behavior claims are changed.
|
|
118
130
|
|
|
119
131
|
## Maintenance Checklist
|
|
@@ -13,6 +13,7 @@ import { componentMap } from "./component-map.js";
|
|
|
13
13
|
import { createServer } from "net";
|
|
14
14
|
import chalk from "chalk";
|
|
15
15
|
import { networkInterfaces } from "os";
|
|
16
|
+
import caspianConfig from "../caspian.config.json";
|
|
16
17
|
|
|
17
18
|
const { __dirname } = getFileMeta();
|
|
18
19
|
const bs: BrowserSyncInstance = browserSync.create();
|
|
@@ -24,14 +25,48 @@ let lastChangedFile: string | null = null;
|
|
|
24
25
|
let pythonPort = 0;
|
|
25
26
|
let bsPort = 0;
|
|
26
27
|
|
|
27
|
-
function
|
|
28
|
+
function getReservedPorts(): Set<number> {
|
|
29
|
+
const reservedPorts = new Set<number>();
|
|
30
|
+
|
|
31
|
+
if (!caspianConfig.mcp) {
|
|
32
|
+
return reservedPorts;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const mcpSpecPath = join(__dirname, "..", "src", "lib", "mcp", "fastmcp.json");
|
|
36
|
+
if (!existsSync(mcpSpecPath)) {
|
|
37
|
+
return reservedPorts;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const mcpSpec = JSON.parse(readFileSync(mcpSpecPath, "utf-8"));
|
|
42
|
+
const reservedPort = Number(mcpSpec?.deployment?.port);
|
|
43
|
+
|
|
44
|
+
if (Number.isInteger(reservedPort) && reservedPort > 0) {
|
|
45
|
+
reservedPorts.add(reservedPort);
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Ignore malformed local MCP config and fall back to dynamic allocation.
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return reservedPorts;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getAvailablePort(
|
|
55
|
+
startPort: number,
|
|
56
|
+
reservedPorts: Set<number> = new Set(),
|
|
57
|
+
): Promise<number> {
|
|
28
58
|
return new Promise((resolve) => {
|
|
59
|
+
if (reservedPorts.has(startPort)) {
|
|
60
|
+
resolve(getAvailablePort(startPort + 1, reservedPorts));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
29
64
|
const server = createServer();
|
|
30
65
|
server.listen(startPort, () => {
|
|
31
66
|
server.close(() => resolve(startPort));
|
|
32
67
|
});
|
|
33
68
|
server.on("error", () => {
|
|
34
|
-
resolve(getAvailablePort(startPort + 1));
|
|
69
|
+
resolve(getAvailablePort(startPort + 1, reservedPorts));
|
|
35
70
|
});
|
|
36
71
|
});
|
|
37
72
|
}
|
|
@@ -134,8 +169,10 @@ const publicPipeline = new DebouncedWorker(
|
|
|
134
169
|
);
|
|
135
170
|
|
|
136
171
|
(async () => {
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
const reservedPorts = getReservedPorts();
|
|
173
|
+
|
|
174
|
+
bsPort = await getAvailablePort(5090, reservedPorts);
|
|
175
|
+
pythonPort = await getAvailablePort(5200, reservedPorts);
|
|
139
176
|
|
|
140
177
|
updateRouteFilesCache();
|
|
141
178
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { isAbsolute, join } from "path";
|
|
3
|
+
import { createServer } from "net";
|
|
3
4
|
import caspianConfig from "../caspian.config.json";
|
|
4
5
|
import { createRestartableProcess, onExit } from "./utils.js";
|
|
5
6
|
|
|
@@ -36,11 +37,79 @@ function getServerSpec(): string | null {
|
|
|
36
37
|
return null;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
function
|
|
40
|
+
function getServerSpecPath(serverSpec: string): string | null {
|
|
41
|
+
if (serverSpec.startsWith("http://") || serverSpec.startsWith("https://")) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return isAbsolute(serverSpec) ? serverSpec : join(projectRoot, serverSpec);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readDeploymentConfig(serverSpec: string): Record<string, unknown> {
|
|
49
|
+
const serverSpecPath = getServerSpecPath(serverSpec);
|
|
50
|
+
if (!serverSpecPath || !existsSync(serverSpecPath)) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const rawConfig = JSON.parse(readFileSync(serverSpecPath, "utf-8"));
|
|
56
|
+
if (rawConfig && typeof rawConfig === "object") {
|
|
57
|
+
const deployment = rawConfig.deployment;
|
|
58
|
+
if (deployment && typeof deployment === "object") {
|
|
59
|
+
return deployment as Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore malformed specs and allow FastMCP to validate them.
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getPreferredHost(serverSpec: string): string {
|
|
70
|
+
return (
|
|
71
|
+
process.env.MCP_HOST?.trim() ||
|
|
72
|
+
String(readDeploymentConfig(serverSpec).host || "").trim() ||
|
|
73
|
+
"127.0.0.1"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getPreferredPort(serverSpec: string): number | null {
|
|
78
|
+
const rawPort =
|
|
79
|
+
process.env.MCP_PORT?.trim() ||
|
|
80
|
+
String(readDeploymentConfig(serverSpec).port || "").trim();
|
|
81
|
+
|
|
82
|
+
if (!rawPort) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const port = Number(rawPort);
|
|
87
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return port;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function findAvailablePort(startPort: number, host: string): Promise<number> {
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
const server = createServer();
|
|
97
|
+
|
|
98
|
+
server.listen(startPort, host, () => {
|
|
99
|
+
server.close(() => resolve(startPort));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
server.on("error", () => {
|
|
103
|
+
resolve(findAvailablePort(startPort + 1, host));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildArgs(serverSpec: string, portOverride?: string): string[] {
|
|
40
109
|
const args = ["run", serverSpec, "--no-banner"];
|
|
41
110
|
const transport = process.env.MCP_TRANSPORT?.trim();
|
|
42
111
|
const host = process.env.MCP_HOST?.trim();
|
|
43
|
-
const port = process.env.MCP_PORT?.trim();
|
|
112
|
+
const port = portOverride || process.env.MCP_PORT?.trim();
|
|
44
113
|
const path = process.env.MCP_PATH?.trim();
|
|
45
114
|
const logLevel = process.env.MCP_LOG_LEVEL?.trim();
|
|
46
115
|
|
|
@@ -133,12 +202,24 @@ if (!serverSpec) {
|
|
|
133
202
|
process.exit(0);
|
|
134
203
|
}
|
|
135
204
|
|
|
205
|
+
const preferredHost = getPreferredHost(serverSpec);
|
|
206
|
+
const preferredPort = getPreferredPort(serverSpec);
|
|
207
|
+
const resolvedPort = preferredPort
|
|
208
|
+
? await findAvailablePort(preferredPort, preferredHost)
|
|
209
|
+
: null;
|
|
210
|
+
|
|
211
|
+
if (preferredPort && resolvedPort && preferredPort !== resolvedPort) {
|
|
212
|
+
console.log(
|
|
213
|
+
`[mcp] Port ${preferredPort} is unavailable on ${preferredHost}, using ${resolvedPort} instead.`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
136
217
|
const handleMcpOutput = createMcpOutputHandler();
|
|
137
218
|
|
|
138
219
|
const runner = createRestartableProcess({
|
|
139
220
|
name: "mcp",
|
|
140
221
|
cmd: fastMcpCommand,
|
|
141
|
-
args: buildArgs(serverSpec),
|
|
222
|
+
args: buildArgs(serverSpec, resolvedPort ? String(resolvedPort) : undefined),
|
|
142
223
|
startMessage: "[mcp] Starting MCP server...",
|
|
143
224
|
onStdout: createLineHandler(handleMcpOutput),
|
|
144
225
|
onStderr: createLineHandler(handleMcpOutput),
|