Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/actions.lock
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ workflows:
- 'actions/checkout@v6.0.2'
'.github/workflows/publish.yml':
- 'actions/checkout@v6.0.2'
- 'actions/setup-node@v4.4.0'
- 'denoland/setup-deno@v2.0.4'
- 'oven-sh/setup-bun@v2.2.0'
'.github/workflows/push-email-notify.yml':
- 'hyperpolymath/smtp-notify-action@v0.2.0'
'.github/workflows/readme-derive.yml': []
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,18 +209,18 @@ jobs:
fail-fast: false
matrix:
include:
- runtime: node
unit: node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js mcp-bridge/tests/path_claims_test.js mcp-bridge/tests/routing_args_test.js
boot: node mcp-bridge/main.js
- runtime: bun
unit: bun test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js mcp-bridge/tests/path_claims_test.js mcp-bridge/tests/routing_args_test.js
boot: bun mcp-bridge/main.js
- runtime: deno
unit: deno test --allow-read --allow-env --allow-run --allow-net mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js mcp-bridge/tests/path_claims_test.js mcp-bridge/tests/routing_args_test.js
# Scoped perms matching main.js's shebang, NOT -A: the boot smoke
# must exercise the exact permission set a real install uses, or it
# would mask a missing-grant bug that scoped users would hit.
boot: deno run --allow-net --allow-env --allow-read mcp-bridge/main.js
- runtime: bun
unit: bun test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js mcp-bridge/tests/path_claims_test.js mcp-bridge/tests/routing_args_test.js
boot: bun mcp-bridge/main.js
- runtime: node
unit: node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/http_transport_test.js mcp-bridge/tests/path_claims_test.js mcp-bridge/tests/routing_args_test.js
boot: node mcp-bridge/main.js

steps:
- name: Checkout
Expand Down
37 changes: 21 additions & 16 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# This workflow is managed by gh actions-lock.
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
#
# Publish workflow — publishes to npm, the official MCP Server Registry, and JSR on version tag push (v*).
# Publish workflow — publishes via Bun, the official MCP Server Registry, and Deno/JSR on version tag push (v*).
# Runtime hierarchy: Bun first, followed by Deno/pnpm, and finally npm/yarn.
# Requires NPM_TOKEN and JSR_TOKEN repository secrets; MCP Registry uses GitHub OIDC (zero secrets).
name: Publish

Expand All @@ -18,8 +19,8 @@ permissions:
contents: read

jobs:
publish-npm:
name: Publish to npm & MCP Registry
publish-bun:
name: Publish via Bun & MCP Registry
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
Expand All @@ -28,22 +29,22 @@ jobs:
steps:
- uses: actions/checkout@v6.0.2

- name: Setup Node.js
uses: actions/setup-node@v4.4.0
- name: Setup Bun
uses: oven-sh/setup-bun@v2.2.0
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
bun-version: '1.x'

- name: Verify package and server.json versions match tag
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PKG_NAME=$(node -p "require('./package.json').name")
PKG_VERSION=$(node -p "require('./package.json').version")
MCP_NAME=$(node -p "require('./package.json').mcpName")
SRV_VERSION=$(node -p "require('./server.json').version")
SRV_NAME=$(node -p "require('./server.json').name")
SRV_PKG_ID=$(node -p "require('./server.json').packages?.[0]?.identifier ?? ''")
SRV_PKG_VERSION=$(node -p "require('./server.json').packages?.[0]?.version ?? ''")
PKG_NAME=$(bun -e "console.log(require('./package.json').name)")
PKG_VERSION=$(bun -e "console.log(require('./package.json').version)")
MCP_NAME=$(bun -e "console.log(require('./package.json').mcpName)")
SRV_VERSION=$(bun -e "console.log(require('./server.json').version)")
SRV_NAME=$(bun -e "console.log(require('./server.json').name)")
SRV_PKG_ID=$(bun -e "console.log(require('./server.json').packages?.[0]?.identifier ?? '')")
SRV_PKG_VERSION=$(bun -e "console.log(require('./server.json').packages?.[0]?.version ?? '')")
SRV_RUNTIME_HINT=$(bun -e "console.log(require('./server.json').packages?.[0]?.runtimeHint ?? '')")
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) does not match package.json version ($PKG_VERSION)"
exit 1
Expand All @@ -64,6 +65,10 @@ jobs:
echo "::error::Tag version ($TAG_VERSION) does not match server.json packages[0].version ($SRV_PKG_VERSION)"
exit 1
fi
if [ "$SRV_RUNTIME_HINT" != "bunx" ]; then
echo "::error::server.json packages[0].runtimeHint ($SRV_RUNTIME_HINT) must be 'bunx'"
exit 1
fi

- name: Install mcp-publisher
run: |
Expand All @@ -77,8 +82,8 @@ jobs:
- name: Validate MCP manifest
run: /tmp/mcp-publisher validate server.json

- name: Publish to npm
run: npm publish --provenance --access public
- name: Publish package via Bun
run: NPM_CONFIG_TOKEN="$NODE_AUTH_TOKEN" bun publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Expand Down
4 changes: 2 additions & 2 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"mcpServers": {
"boj-server": {
"command": "npx",
"args": ["-y", "@hyperpolymath/boj-server@latest"],
"command": "bunx",
"args": ["@hyperpolymath/boj-server@latest"],
"env": {
"BOJ_URL": "http://localhost:7700"
}
Expand Down
4 changes: 2 additions & 2 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ WORKDIR /app
COPY package.json /app/
COPY mcp-bridge/ /app/mcp-bridge/

# No npm install needed — zero dependencies
# No package install needed — zero dependencies
USER node

ENTRYPOINT ["node", "mcp-bridge/main.js"]
ENTRYPOINT ["/usr/local/bin/bun", "mcp-bridge/main.js"]
2 changes: 1 addition & 1 deletion container/Containerfile.mcp-bridge
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ USER appuser
# Health: just check process alive (stdin bridge has no HTTP endpoint)
ENV BOJ_URL=http://localhost:7700

ENTRYPOINT ["deno", "run", "--allow-net", "/app/mcp-bridge/main.js"]
ENTRYPOINT ["bun", "/app/mcp-bridge/main.js"]
7 changes: 4 additions & 3 deletions mcp-bridge/lib/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
//
// BoJ Server — Runtime abstraction layer
//
// Provides a unified interface for Deno and Node.js environments.
// Supports the "Deno > Bun > NPM" hierarchy while maintaining
// Provides a unified interface for Bun, Deno, and Node.js environments.
// Supports the "Bun > Deno/pnpm > npm/yarn" hierarchy while maintaining
// compatibility with Node-only MCP clients like Glama.

import { writeSync as writeFdSync } from "node:fs";

const isBun = typeof globalThis.Bun !== "undefined";
const isDeno = typeof globalThis.Deno !== "undefined";

/** @type {{ get: (name: string) => string|undefined }} */
Expand Down Expand Up @@ -51,4 +52,4 @@ export const stderr = {
}
};

export { isDeno };
export { isBun, isDeno };
4 changes: 3 additions & 1 deletion mcp-bridge/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
// BOJ_TRANSPORT=http listen on BOJ_HTTP_PORT (default 7780)
// BOJ_TRANSPORT=both run both simultaneously
//
// Usage:
// Usage (preferred order: Bun > Deno/pnpm > Node/npm):
// bun mcp-bridge/main.js
// deno run --allow-net --allow-env --allow-read mcp-bridge/main.js
// node mcp-bridge/main.js

import { env, stdout } from "./lib/runtime.js";
import { sanitizeErrorMessage } from "./lib/security.js";
Expand Down
1 change: 1 addition & 0 deletions mcp-bridge/tests/dispatch_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ test("server.json and package.json satisfy MCP Registry constraints", () => {
assert.ok(npmPkg, "server.json must declare an npm package entry");
assert.equal(npmPkg.identifier, pkg.name);
assert.equal(npmPkg.version, pkg.version);
assert.equal(npmPkg.runtimeHint, "bunx", "server.json package entry must prefer bunx as runtimeHint");
assert.equal(npmPkg.transport?.type, "stdio");
});

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@
"boj-server": "mcp-bridge/main.js"
},
"scripts": {
"start": "deno run -A mcp-bridge/main.js",
"test": "node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js"
"start": "if command -v bun >/dev/null 2>&1; then exec bun mcp-bridge/main.js; elif command -v deno >/dev/null 2>&1; then exec deno run --allow-net --allow-env --allow-read mcp-bridge/main.js; else exec node mcp-bridge/main.js; fi",
"start:bun": "bun mcp-bridge/main.js",
"start:deno": "deno run --allow-net --allow-env --allow-read mcp-bridge/main.js",
"start:node": "node mcp-bridge/main.js",
"test": "if command -v bun >/dev/null 2>&1; then bun test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js; elif command -v deno >/dev/null 2>&1; then deno test --allow-read --allow-env --allow-run --allow-net mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js; else node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js; fi",
"test:bun": "bun test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js",
"test:deno": "deno test --allow-read --allow-env --allow-run --allow-net mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js",
"test:node": "node --test mcp-bridge/tests/dispatch_test.js mcp-bridge/tests/routing_args_test.js mcp-bridge/tests/declared_args_test.js"
},
"engines": {
"bun": ">=1.0.0",
"node": ">=18.0.0"
}
}
18 changes: 16 additions & 2 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@hyperpolymath/boj-server",
"version": "0.5.0",
"runtimeHint": "npx",
"runtimeHint": "bunx",
"transport": {
"type": "stdio"
},
Expand Down Expand Up @@ -99,5 +99,19 @@
}
]
}
]
],
"_meta": {
"io.modelcontextprotocol.registry/publisher-provided": {
"io.github.hyperpolymath": {
"runtimePreference": [
"bunx",
"deno",
"pnpm",
"npx",
"yarn"
],
"jsrPackage": "jsr:@hyperpolymath/boj-server"
}
}
}
}
Loading