@feniix/bridgekit 0.11.0 → 0.13.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/CHANGELOG.md +63 -0
- package/README.md +40 -4
- package/dist/src/adapters/pi.d.ts +1 -1
- package/dist/src/bin-wrapper-internal.d.ts +14 -0
- package/dist/src/bin-wrapper-internal.js +1 -1
- package/llms.txt +19 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,69 @@ All notable changes to `@feniix/bridgekit` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.13.0] - 2026-05-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- New `buildStdio` option on `BinWrapperOptions`
|
|
12
|
+
(`@feniix/bridgekit/bin-wrapper`). Default `"inherit"` preserves
|
|
13
|
+
existing behavior. **MCP stdio server bins should pass
|
|
14
|
+
`["ignore", "inherit", "inherit"]`** — the build subprocess's stdout
|
|
15
|
+
otherwise contaminates the parent's JSON-RPC framing on its own
|
|
16
|
+
stdout. The default-`"inherit"` behavior was a latent risk for the
|
|
17
|
+
two current consumers that adopted `runBinWrapper` (works today only
|
|
18
|
+
because `npm run <script> --silent` happens to be quiet under tsc) and
|
|
19
|
+
an active blocker for `pi-exa`, which could not migrate from its
|
|
20
|
+
hand-rolled wrapper. Resolves
|
|
21
|
+
[#59](https://github.com/feniix/bridgekit/issues/59).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `PiToolDefinition.promptGuidelines` (the internal tool-shape declared
|
|
26
|
+
in `src/adapters/pi.ts` and exposed via
|
|
27
|
+
`PiToolRegistration["registerTool"]`'s parameter) widened from
|
|
28
|
+
`readonly string[]` to `string[]` to close a contravariance gap with
|
|
29
|
+
pi-coding-agent's `ExtensionAPI`. The runtime contract is unchanged —
|
|
30
|
+
`registerPiTools` has spread-copied the source array into a fresh
|
|
31
|
+
`string[]` before handing it to `pi.registerTool` since 0.9.1
|
|
32
|
+
(PR #48); only the type declaration catches up to reality. Consumers
|
|
33
|
+
(`pi-exa`, `pi-code-reasoning`, `pi-sequential-thinking`) can drop
|
|
34
|
+
the `pi as unknown as PiToolRegistration` cast at their
|
|
35
|
+
`registerPiTools` call sites.
|
|
36
|
+
`PortableTool.hostExtras.pi.promptGuidelines` (the consumer-facing
|
|
37
|
+
metadata type) stays `readonly string[]` — that's the right contract
|
|
38
|
+
for immutable consumer-owned data that bridgekit reads. Resolves
|
|
39
|
+
[#60](https://github.com/feniix/bridgekit/issues/60).
|
|
40
|
+
|
|
41
|
+
## [0.12.0] - 2026-05-27
|
|
42
|
+
|
|
43
|
+
### Documentation
|
|
44
|
+
|
|
45
|
+
- `README.md` gets a full `bin-wrapper` adapter section under "API
|
|
46
|
+
reference" — usage example, options breakdown, `runServer`
|
|
47
|
+
contract, trusted-literal security note, and the missing-entry
|
|
48
|
+
behavior matrix. Previously the subpath had a single line in the
|
|
49
|
+
entrypoint table and zero body documentation. Closes the largest
|
|
50
|
+
gap surfaced by the post-0.11.0 doc audit.
|
|
51
|
+
- `README.md` "Why" bullet and "Packaging" section updated for the
|
|
52
|
+
four-entrypoint shape and the `runBinWrapper` recommendation
|
|
53
|
+
(replaces the pre-0.11.0 manual checked-in-wrapper recipe).
|
|
54
|
+
- `llms.txt` rewrites the "Mixed source-loaded hosts and compiled
|
|
55
|
+
MCP bins" section to lead with `runBinWrapper`. The 8-bullet
|
|
56
|
+
manual-wrapper recipe is demoted to a fallback bullet for
|
|
57
|
+
authors writing their own wrapper.
|
|
58
|
+
- `dist/src/bin-wrapper.d.ts` added to the published-declarations
|
|
59
|
+
lists in `README.md` and `llms.txt`.
|
|
60
|
+
- `docs/README.md` lists `docs/rfc-host-extras.md` in the
|
|
61
|
+
maintainer-docs catalog. The RFC is JSDoc-linked from
|
|
62
|
+
`src/core/define-tool.ts` and was missing from the catalog.
|
|
63
|
+
- `src/adapters/mcp.typecheck.ts` fixes a wrong version stamp on
|
|
64
|
+
the `signalFromExtra` adversarial-pin comment (was `0.10.0 (#3)`,
|
|
65
|
+
should be `0.11.0 (#3)`). The pin's code is correct; only the
|
|
66
|
+
comment was wrong.
|
|
67
|
+
|
|
68
|
+
No API or behavior changes.
|
|
69
|
+
|
|
7
70
|
## [0.11.0] - 2026-05-27
|
|
8
71
|
|
|
9
72
|
### Added
|
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ This package is ESM-only and supports Node.js 22.19.0 or newer. Published module
|
|
|
66
66
|
- Define a tool once; ship it to pi and MCP without per-host forks.
|
|
67
67
|
- Host-neutral tool files: no pi or MCP SDK imports in the tool definition itself.
|
|
68
68
|
- TypeBox schemas pass through to MCP `inputSchema` directly — no JSON Schema conversion step.
|
|
69
|
-
- Import-passive, `sideEffects: false`,
|
|
69
|
+
- Import-passive, `sideEffects: false`, four-entrypoint split (`.`, `./pi`, `./mcp`, `./bin-wrapper`) so pi-only consumers do not pull the MCP SDK and vice versa.
|
|
70
70
|
- Conformance-tested public surface: a packed-install smoke test enforces the runtime export set, deep-import rejection, and type-level strictness against the installed declarations.
|
|
71
71
|
|
|
72
72
|
## API reference
|
|
@@ -233,6 +233,42 @@ Portable validation failures and portable `isError: true` results return `CallTo
|
|
|
233
233
|
|
|
234
234
|
The two adapters now read in parallel: invalid args and portable `isError` results return `{ isError: true }` from both hosts by default, and the same result-guard helpers (`isValidationFailure`, `isDomainFailure`) narrow them on either side.
|
|
235
235
|
|
|
236
|
+
### bin-wrapper (since 0.11.0)
|
|
237
|
+
|
|
238
|
+
The `bin-wrapper` subpath ships a helper for npm `bin` scripts that load a compiled MCP entrypoint and build it on first invocation when the compiled output is missing. Replaces the ~25-line "resolve dist path → spawn build if missing → import and run" boilerplate that downstream consumers with mixed source-loaded pi + compiled MCP packages previously hand-rolled.
|
|
239
|
+
|
|
240
|
+
```js
|
|
241
|
+
#!/usr/bin/env node
|
|
242
|
+
import { runBinWrapper } from "@feniix/bridgekit/bin-wrapper";
|
|
243
|
+
|
|
244
|
+
await runBinWrapper({
|
|
245
|
+
metaUrl: import.meta.url,
|
|
246
|
+
mcpEntry: "dist/extensions/mcp-server.js",
|
|
247
|
+
buildScript: "build:mcp",
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Options:
|
|
252
|
+
|
|
253
|
+
- `metaUrl` (required): `import.meta.url` of the bin script. Used to locate the package root (the bin's parent directory).
|
|
254
|
+
- `mcpEntry` (required): path to the compiled MCP entry, relative to the package root (e.g. `"dist/extensions/mcp-server.js"`).
|
|
255
|
+
- `buildScript` (required): npm script to invoke when the entry is missing (e.g. `"build:mcp"`).
|
|
256
|
+
- `buildTimeoutMs` (optional, default `60_000`): timeout for the build subprocess. Distinct "Build timed out…" diagnostic fires when exceeded.
|
|
257
|
+
- `logPrefix` (optional, default `"bridgekit-bin"`): prefix on the "Failed to build…" diagnostic.
|
|
258
|
+
- `buildStdio` (optional, default `"inherit"`): `stdio` mode passed to `spawnSync` when the build script runs. **MCP stdio server bins should pass `["ignore", "inherit", "inherit"]`** so the build subprocess's stdout cannot contaminate the parent's JSON-RPC framing channel (`process.stdout`). stderr stays inherited so build diagnostics remain visible.
|
|
259
|
+
|
|
260
|
+
The MCP entry module must export `async function runServer(): Promise<void>`. Consumers with a different export name can alias on export.
|
|
261
|
+
|
|
262
|
+
**Security: `mcpEntry` and `buildScript` must be literal strings in the caller's source.** The helper joins `mcpEntry` onto the resolved package root and dynamically `import()`s it, and it passes `buildScript` to `spawnSync` (with `shell: true` on Windows where `&`, `|`, and `^` are shell metacharacters). Sourcing either from CLI args or environment variables exposes arbitrary-file import and Windows command injection.
|
|
263
|
+
|
|
264
|
+
Behavior on missing entry:
|
|
265
|
+
|
|
266
|
+
- Build emits the entry → import and call `runServer()`.
|
|
267
|
+
- Build exits non-zero but the entry exists → recover (import and call `runServer()`).
|
|
268
|
+
- Build exits 0 but the entry is still missing → exit with code 1, generic "Failed to build…" diagnostic.
|
|
269
|
+
- Build exits non-zero with entry missing → exit with the build's status code, generic diagnostic.
|
|
270
|
+
- Build killed by timeout (SIGTERM) → exit 1, distinct timeout diagnostic naming `buildTimeoutMs`.
|
|
271
|
+
|
|
236
272
|
## Best practices
|
|
237
273
|
|
|
238
274
|
Tool definition best practices:
|
|
@@ -258,7 +294,7 @@ Package and release checklist:
|
|
|
258
294
|
- Avoid `workspace:` or `file:` dependency ranges in publishable packages.
|
|
259
295
|
- Avoid dangling `sourceMappingURL` comments: publish maps and useful sources together, or disable source maps for package builds.
|
|
260
296
|
- For MCP stdio bins, ensure the executable entrypoint starts with a Node shebang, has executable mode (`chmod +x` or equivalent), and is included by `npm pack --dry-run --json`.
|
|
261
|
-
- If an npm-launched bin depends on generated output,
|
|
297
|
+
- If an npm-launched bin depends on generated output, use `runBinWrapper` from `@feniix/bridgekit/bin-wrapper` (since 0.11.0) — it resolves the package-local generated entry, runs the package-local build when output is missing in workspace/local execution, preserves build failures, and distinguishes timeout from build error in its diagnostic. The bin script becomes a three-line invocation; no hand-rolled wrapper needed.
|
|
262
298
|
- If a package keeps a source-loaded host entrypoint (for example a pi extension source file), use a package-local MCP build behind that wrapper and narrow the build to the MCP entrypoint plus shared host-neutral modules.
|
|
263
299
|
- Declare a compatible Node engine (`>=22.19.0`) in downstream packages that expose BridgeKit-powered MCP bins.
|
|
264
300
|
- Run `npm run check`, `npm run test`, `npm run pack:dry-run`, and `npm run package-smoke` before publishing.
|
|
@@ -272,5 +308,5 @@ Read these files in order:
|
|
|
272
308
|
|
|
273
309
|
1. `README.md` — public API, contracts, and best practices.
|
|
274
310
|
2. `llms.txt` — compact agent-facing usage rules and anti-patterns.
|
|
275
|
-
3. `examples/README.md` — copyable layouts for shared tools, pi extensions, MCP stdio servers,
|
|
276
|
-
4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, and `dist/src/
|
|
311
|
+
3. `examples/README.md` — copyable layouts for shared tools, pi extensions, MCP stdio servers, per-host metadata via `hostExtras`, and bin wrappers via `runBinWrapper`.
|
|
312
|
+
4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, `dist/src/mcp.d.ts`, and `dist/src/bin-wrapper.d.ts` — canonical installed-package type contracts. In a source checkout, the matching `src/` files contain the same implementation context.
|
|
@@ -20,7 +20,7 @@ type PiToolDefinition = {
|
|
|
20
20
|
parameters: TSchema;
|
|
21
21
|
execute(toolCallId: string, params: unknown, signal?: AbortSignal, onUpdate?: (update: PiToolUpdate) => void, ctx?: unknown): Promise<PiToolResult>;
|
|
22
22
|
promptSnippet?: string;
|
|
23
|
-
promptGuidelines?:
|
|
23
|
+
promptGuidelines?: string[];
|
|
24
24
|
};
|
|
25
25
|
export type PiToolRegistration = {
|
|
26
26
|
registerTool(tool: PiToolDefinition): unknown;
|
|
@@ -30,6 +30,20 @@ export interface BinWrapperOptions {
|
|
|
30
30
|
buildScript: string;
|
|
31
31
|
buildTimeoutMs?: number;
|
|
32
32
|
logPrefix?: string;
|
|
33
|
+
/**
|
|
34
|
+
* stdio mode passed to `spawnSync` when the build script runs. Defaults to
|
|
35
|
+
* `"inherit"`.
|
|
36
|
+
*
|
|
37
|
+
* **MCP stdio server bins must override this.** `process.stdout` IS the
|
|
38
|
+
* JSON-RPC channel for an MCP stdio server; any output the build subprocess
|
|
39
|
+
* emits to stdout (npm warnings, postinstall scripts, tsc diagnostics) flows
|
|
40
|
+
* directly into the parent's stdout via `stdio: "inherit"` and contaminates
|
|
41
|
+
* JSON-RPC framing. Pass `["ignore", "inherit", "inherit"]` to route the
|
|
42
|
+
* build's stdout to `/dev/null` while keeping stderr visible.
|
|
43
|
+
*
|
|
44
|
+
* @default "inherit"
|
|
45
|
+
*/
|
|
46
|
+
buildStdio?: SpawnSyncOptions["stdio"];
|
|
33
47
|
}
|
|
34
48
|
export interface BinWrapperDeps {
|
|
35
49
|
spawnSync: BinWrapperSpawnSync;
|
|
@@ -20,7 +20,7 @@ export async function runBinWrapperWithDeps(options, deps) {
|
|
|
20
20
|
const timeoutMs = options.buildTimeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS;
|
|
21
21
|
const build = deps.spawnSync("npm", ["run", options.buildScript, "--silent"], {
|
|
22
22
|
cwd: packageRoot,
|
|
23
|
-
stdio: "inherit",
|
|
23
|
+
stdio: options.buildStdio ?? "inherit",
|
|
24
24
|
shell: process.platform === "win32",
|
|
25
25
|
timeout: timeoutMs,
|
|
26
26
|
});
|
package/llms.txt
CHANGED
|
@@ -7,7 +7,7 @@ Use this package when you need to define a tool once and expose it through both
|
|
|
7
7
|
1. `README.md` — public API, contracts, best practices, packaging notes.
|
|
8
8
|
2. `llms.txt` — compact agent-facing usage rules and anti-patterns.
|
|
9
9
|
3. `examples/README.md` — copyable end-to-end layouts for shared tools, pi extension wiring, MCP stdio server wiring, and per-host metadata via `hostExtras`.
|
|
10
|
-
4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, and `dist/src/
|
|
10
|
+
4. Published declarations such as `dist/src/index.d.ts`, `dist/src/pi.d.ts`, `dist/src/mcp.d.ts`, and `dist/src/bin-wrapper.d.ts` — canonical installed-package type contracts. In a source checkout, the matching `src/` files contain implementation context.
|
|
11
11
|
|
|
12
12
|
## Import map
|
|
13
13
|
|
|
@@ -104,12 +104,27 @@ Some hosts, such as pi in source-extension packages, may intentionally load Type
|
|
|
104
104
|
|
|
105
105
|
- Keep the host source entrypoint if that is the package convention, e.g. `pi.extensions: ["./extensions/index.ts"]`.
|
|
106
106
|
- Add a package-local MCP build and point npm `bin` at a checked-in wrapper under `bin/`, not directly at generated `dist/` output.
|
|
107
|
-
-
|
|
107
|
+
- **Use the built-in `runBinWrapper` helper** (since 0.11.0) instead of a hand-rolled wrapper. It resolves the package-local generated entry, runs the package-local build when output is missing in workspace/local execution, preserves non-zero build failures, recovers when the build emits the entry despite a non-zero exit, and emits a distinct timeout diagnostic when `spawnSync` is killed by `buildTimeoutMs`:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
#!/usr/bin/env node
|
|
111
|
+
import { runBinWrapper } from "@feniix/bridgekit/bin-wrapper";
|
|
112
|
+
await runBinWrapper({
|
|
113
|
+
metaUrl: import.meta.url,
|
|
114
|
+
mcpEntry: "dist/extensions/mcp-server.js",
|
|
115
|
+
buildScript: "build:mcp",
|
|
116
|
+
// MCP stdio bins: route the build subprocess's stdout to /dev/null so it
|
|
117
|
+
// cannot contaminate the parent's JSON-RPC framing on process.stdout.
|
|
118
|
+
buildStdio: ["ignore", "inherit", "inherit"],
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`mcpEntry` and `buildScript` must be literal strings — sourcing them from CLI args or env vars opens arbitrary-file import and Windows command injection. The MCP entry module must export `async function runServer(): Promise<void>`. `buildStdio` defaults to `"inherit"`; MCP stdio server bins should override to `["ignore", "inherit", "inherit"]` (since 0.13.0) because `process.stdout` is the JSON-RPC channel and the build subprocess otherwise contaminates framing.
|
|
108
123
|
- Narrow the MCP build to the MCP entrypoint and shared host-neutral modules; avoid compiling pi adapter entrypoints into the standalone MCP path.
|
|
109
124
|
- Put runtime imports used by the compiled MCP bin in `dependencies`, not only peers or dev dependencies.
|
|
110
125
|
- Declare BridgeKit's Node engine requirement in the downstream package (`>=22.19.0`).
|
|
111
|
-
- Ensure the
|
|
112
|
-
-
|
|
126
|
+
- Ensure the bin script is executable (`chmod +x` or equivalent) and verify the mode with `npm pack --dry-run --json`.
|
|
127
|
+
- If you write your own wrapper instead of using `runBinWrapper`, test the four behavior modes: existing output, missing output that the build creates, failed builds (non-zero exit), and builds that exit successfully without creating the expected file.
|
|
113
128
|
|
|
114
129
|
## Anti-patterns
|
|
115
130
|
|