@hol-org/hashnet-mcp 1.0.20 → 1.0.21

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/package.json CHANGED
@@ -1,32 +1,27 @@
1
1
  {
2
2
  "name": "@hol-org/hashnet-mcp",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "description": "HOL MCP server for agentic search, discovery, registration, chat, and more with the Hashgraph Online Registry Broker.",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.cjs",
8
+ "types": "./dist/index.d.cts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "default": "./dist/index.js"
11
+ "types": "./dist/index.d.cts",
12
+ "import": "./dist/index.cjs",
13
+ "default": "./dist/index.cjs"
14
14
  },
15
15
  "./cli/up": {
16
- "import": "./dist/cli/up.js",
17
- "default": "./dist/cli/up.js"
16
+ "import": "./dist/cli/up.cjs",
17
+ "default": "./dist/cli/up.cjs"
18
18
  }
19
19
  },
20
20
  "bin": {
21
- "hashnet-mcp": "./dist/cli/up.js"
21
+ "hashnet-mcp": "./dist/cli/up.cjs"
22
22
  },
23
23
  "files": [
24
- "dist",
25
- "deploy",
26
- "examples",
27
- "README.md",
28
- "AGENTS.md",
29
- "todo.md"
24
+ "dist"
30
25
  ],
31
26
  "scripts": {
32
27
  "dev:stdio": "MCP_TRANSPORT=stdio tsx src/index.ts",
@@ -80,29 +75,33 @@
80
75
  },
81
76
  "packageManager": "pnpm@10.10.0",
82
77
  "devDependencies": {
83
- "@types/node": "^24.10.0",
84
- "@vitest/coverage-v8": "^2.1.4",
85
- "tsup": "^8.5.0",
86
- "tsx": "^4.20.6",
87
- "typescript": "^5.9.3",
88
- "vitest": "^2.1.4"
89
- },
90
- "dependencies": {
91
78
  "@hashgraphonline/standards-sdk": "^0.1.139",
92
79
  "@hono/node-server": "^1.19.6",
93
80
  "@modelcontextprotocol/sdk": "^1.21.1",
81
+ "@types/node": "^24.10.0",
82
+ "@vitest/coverage-v8": "^2.1.4",
94
83
  "bottleneck": "^2.19.5",
95
84
  "dotenv": "^17.2.3",
96
85
  "fastmcp": "^3.22.0",
97
86
  "hono": "^4.10.5",
98
87
  "ioredis": "^5.8.2",
99
88
  "pino": "^10.1.0",
89
+ "tsup": "^8.5.0",
90
+ "tsx": "^4.20.6",
91
+ "typescript": "^5.9.3",
100
92
  "undici": "^7.16.0",
93
+ "vitest": "^2.1.4",
101
94
  "zod": "^4.1.12"
102
95
  },
103
- "optionalDependencies": {
96
+ "dependencies": {},
97
+ "peerDependencies": {
104
98
  "better-sqlite3": "^11.5.0"
105
99
  },
100
+ "peerDependenciesMeta": {
101
+ "better-sqlite3": {
102
+ "optional": true
103
+ }
104
+ },
106
105
  "overrides": {
107
106
  "@hashgraphonline/standards-sdk": "^0.1.139"
108
107
  }
package/AGENTS.md DELETED
@@ -1,163 +0,0 @@
1
- # Repository Guidelines
2
-
3
- # **ABSOLUTE PROHIBITION — NO FILE DELETIONS OR GIT REVERTS.** Never delete files, directories, or history. Never run `git revert`, `git reset`, `git checkout`, `git restore`, or any command that removes tracked work. Violations immediately fail the task and forfeit all points.
4
-
5
- # AGENTS.md — Tool Selection (TypeScript)
6
-
7
- - Find files by file name: `fd`
8
- - Find files with path name: `fd -p <file-path>`
9
- - List files in a directory: `fd . <directory>`
10
- - Find files with extension and pattern: `fd -e <extension> <pattern>`
11
- - Find text: `rg`
12
- - Structured code search and codemods: `ast-grep`
13
- - Default languages:
14
- - `.ts` → `ast-grep --lang ts -p '<pattern>'`
15
- - `.tsx` → `ast-grep --lang tsx -p '<pattern>'`
16
- - Common languages:
17
- - Python → `ast-grep --lang python -p '<pattern>'`
18
- - TypeScript → `ast-grep --lang ts -p '<pattern>'`
19
- - TSX (React) → `ast-grep --lang tsx -p '<pattern>'`
20
- - JavaScript → `ast-grep --lang js -p '<pattern>'`
21
- - Rust → `ast-grep --lang rust -p '<pattern>'`
22
- - Bash → `ast-grep --lang bash -p '<pattern>'`
23
- - JSON → `ast-grep --lang json -p '<pattern>'`
24
- - Select among matches: pipe to `fzf`
25
- - JSON: `jq`
26
- - YAML/XML: `yq`
27
-
28
- If `ast-grep` is available, avoid `rg` or `grep` unless a plain-text search is explicitly requested.
29
-
30
- ## Project Structure & Module Organization
31
- The MCP server lives in `src/` with three core modules: `mcp.ts` (tool wiring and metadata), `broker.ts` (the `RegistryBrokerClient` wrapper), and `transports.ts` (stdio plus SSE server built on Hono). Place shared schemas in `src/schemas/` and transport helpers in `src/transports/` if they grow larger. Configuration belongs in `tsconfig.json`, environment defaults in `.env.example`, and any integration fixtures under `examples/`. Keep tests in `tests/` mirroring the source tree so `tests/mcp/tools.spec.ts` maps cleanly to `src/mcp.ts`.
32
-
33
- ## Build, Test, and Development Commands
34
- - `pnpm install` — syncs dependencies, including FastMCP, Hono, and the Hashgraph Online SDK.
35
- - `pnpm dev:stdio` — launches `src/index.ts` with `MCP_TRANSPORT=stdio`, wired for Claude Desktop.
36
- - `pnpm dev:sse` — serves `http://localhost:${PORT}/mcp/stream` via Hono’s SSE transport for Claude Code/Cursor.
37
- - `HTTP_STREAM_PORT` controls the upstream FastMCP HTTP server (defaults to `PORT + 1`). External clients still connect to `http://localhost:${PORT}/mcp/stream` (Streamable HTTP) or `/mcp/sse`.
38
- - `pnpm build` — runs `tsup` (see `tsup.config.ts`) to emit both `dist/index.js` and the NPX CLI bundle.
39
- - `pnpm start` — executes the compiled output (`node dist/index.js`) for production parity.
40
- - `pnpm test --run --coverage` — executes Vitest once with V8 coverage; `pnpm test` stays in watch mode.
41
- - `pnpm quickstart` — interactive DX script that installs deps, copies `.env`, runs smoke tests, and launches your preferred transport (stdio or SSE).
42
- - `pnpm test:tools` — end-to-end harness that connects to the HTTP-stream gateway via the official MCP client (Streamable HTTP transport) and calls every `hol.*` tool using sample payloads (set `TEST_UAID`, `TEST_CHAT_UAID`, `TEST_REGISTRATION_ATTEMPT_ID` to cover UAID/chat flows). The script manages its own server lifecycle; do **not** pass `--spawn` manually.
43
- - `pnpm test:tools:mock` — runs the same harness against the built-in mock MCP server so CI can exercise the exit flow without touching the real Registry Broker.
44
- - `BROKER_PROTOCOL_TOOLS=1` — optional flag to include `hol.listProtocols` / `hol.detectProtocol` in the tool suite; leave unset if your API key or environment doesn't expose those endpoints (the public staging broker returns 404).
45
- - `pnpm workflow:list` / `pnpm workflow:run <name>` — inspect and execute any workflow (pair with `examples/workflows/<workflow>.json` for sample payloads).
46
- - `pnpm workflow:register` — interactive wizard that runs the registration/chat/ops workflows and saves a JSON report (UAID + Claude snippet).
47
- - `pnpm workflow:register:advanced` — guided prompts for `workflow.registerAgentAdvanced` (additional registries + optional credit purchase).
48
- - `pnpm workflow:register:erc8004` — helper around the ERC-8004 workflow (ledger walkthrough + post-registration chat).
49
- - `pnpm mock:broker` — local mock broker for CI/contract testing.
50
- - All scripts load `.env` automatically via `dotenv`, so once you copy `.env.example` you can simply edit the file and re-run commands without re-exporting variables.
51
-
52
- ## Workflows
53
- `pnpm workflow:list` prints every registered pipeline, and each one has a golden-path payload under `examples/workflows/`. Copy the file, replace the placeholder UAIDs/API keys, then run `pnpm workflow:run <name> --payload <file>`.
54
-
55
- **Discovery & Ops**
56
- - `workflow.discovery` and `workflow.erc8004Discovery` (search/vector/namespace lookups).
57
- - `workflow.opsCheck` (stats + metrics + protocols).
58
- - `workflow.registryBrokerShowcase` (discovery → analytics → optional chat).
59
-
60
- **Registration Pipelines**
61
- - `workflow.registerMcp`, `workflow.registerAgentAdvanced`, `workflow.registerAgentErc8004`.
62
- - `workflow.fullRegistration` (discovery → registration → chat → ops).
63
- - `workflow.erc8004X402` and `workflow.x402Registration` (registration funded via X402 with optional chat checks).
64
-
65
- **Credit & Ledger Utilities**
66
- - `workflow.ledgerAuth` (challenge + verify).
67
- - `workflow.x402TopUp` (buy credits via X402).
68
- - `workflow.historyTopUp` (chat compaction + HBAR auto-purchases on 402 errors).
69
-
70
- **Chat & Interop**
71
- - `workflow.chatSmoke` (UAID session lifecycle).
72
- - `workflow.openrouterChat` (discover + ping an OpenRouter model).
73
- - `workflow.agentverseBridge` (relay traffic between a local UAID and Agentverse).
74
-
75
- Runbook examples:
76
-
77
- ```
78
- pnpm workflow:run workflow.registerAgentAdvanced --payload examples/workflows/workflow.registerAgentAdvanced.json
79
- pnpm workflow:run workflow.openrouterChat --payload examples/workflows/workflow.openrouterChat.json --endpoint https://host/mcp/stream
80
- ```
81
-
82
- Each workflow emits a structured report (steps, timings, context) whether executed via MCP or CLI.
83
-
84
- ### Workflow Architecture
85
- - Pipelines live in `src/workflows/`; each file exports a `registerPipeline()` definition.
86
- - `src/workflows/index.ts` imports these modules so they register at startup.
87
- - MCP tools (`workflow.*`) are wired in `src/mcp.ts` and simply call the pipelines.
88
- - For a visual overview, see the "Architecture Overview" section in `README.md`.
89
-
90
- ### Required Environment Variables
91
- - `REGISTRY_BROKER_API_URL` / `REGISTRY_BROKER_API_KEY` — broker endpoint + key (staging/testnet supported).
92
- - `HEDERA_ACCOUNT_ID` / `HEDERA_PRIVATE_KEY` — optional; only set these if you want CLI-driven credit purchases when the key runs dry.
93
- - `WORKFLOW_DRY_RUN=1` — optional guard that makes pipelines skip state-changing broker calls.
94
- - `BROKER_E2E=1` — opt into real broker hits in CI; otherwise the mock broker is used when available.
95
- - `BROKER_AUTO_TOP_UP=1` — opt into automatic broker purchases without HITL prompts (defaults to manual approvals).
96
- - X402 workflows also require EVM wallet details (see `examples/workflows/workflow.x402*.json`) plus any ledger challenge metadata referenced in the payload.
97
- - Tool-suite fixtures (optional): `TEST_UAID`, `TEST_CHAT_UAID`, `TEST_REGISTRATION_ATTEMPT_ID`, and `BROKER_PROTOCOL_TOOLS`. Populate them (see `.env.example`) so `pnpm test:tools` can exercise UAID/chat flows locally or skip protocol checks when the broker doesn’t expose those endpoints; leave blank to rely on auto-discovered UAIDs/attempt IDs from the preceding scenarios.
98
- Workflow pipelines declare their required env vars and will fail fast with a descriptive error if anything is missing; payload-specific secrets (OpenRouter tokens, Agentverse headers, bearer tokens) should be supplied via `.env` or injected by your CLI before invoking the workflow.
99
-
100
- ### Memory (optional)
101
- - Set `MEMORY_ENABLED=1` to enable local memory capture (SQLite by default; see `.env.example` for limits and store selection). When enabled, workflows like `workflow.chatSmoke`, `workflow.openrouterChat`, `workflow.historyTopUp`, `workflow.registryBrokerShowcase`, and `workflow.fullRegistration` will load scoped context (uaid/session/namespace) and append discovery/chat traces. Pass `disableMemory: true` in workflow inputs to opt out even when memory is enabled.
102
-
103
- ### Running `pnpm workflow:register`
104
- 1. The CLI prompts for display name, alias, description, MCP URL, chat message, and report path (defaults provided).
105
- 2. Runs the registration → chat → ops pipelines and prints progress to the console.
106
- 3. Writes the report JSON (defaults to `workflow-register-report.json`) containing:
107
- - UAID (if registration succeeded)
108
- - Pipeline traces (steps + context)
109
- - Claude config snippet pointing at your MCP URL
110
- - Raw pipeline responses for registration/chat/ops
111
-
112
- **Sample report excerpt**
113
-
114
- ```json
115
- {
116
- "uaid": "uaid:registry:abcd-1234",
117
- "pipelines": [
118
- { "name": "workflow.registerMcp", "steps": ["hol.getRegistrationQuote", "hol.registerAgent"] }
119
- ],
120
- "claudeConfig": {
121
- "mcpServers": {
122
- "hashnet": {
123
- "command": "npx",
124
- "args": ["@hol-org/hashnet-mcp@latest", "up", "--transport", "sse"]
125
- }
126
- }
127
- }
128
- }
129
- ```
130
-
131
- The CLI spends the API key’s existing credits first. If the broker rejects the registration (HTTP 402), the script fetches a fresh quote, prints the required/available credits, and prompts for HITL approval before calling `purchaseCreditsWithHbar` (when `HEDERA_*` are configured). It continues only after the broker reports the new balance. Toggle `BROKER_AUTO_TOP_UP=1` if you want the SDK’s legacy auto top-up behaviour without prompts.
132
-
133
- ### Adding / Extending Workflows
134
- 1. Create `src/workflows/<name>.ts`, register the pipeline with metadata + env requirements, and export any helper schemas.
135
- 2. Import it from `src/workflows/index.ts` so registration happens at server boot.
136
- 3. Optional MCP tool: define schemas/handlers in `src/mcp.ts` and wrap responses with `formatPipelineResult`.
137
- 4. Tests belong in `tests/workflows/<name>.spec.ts` (unit) plus CLI hooks in `scripts/workflow-run*.ts` when interactive flags are required.
138
- 5. Document new workflows (README/AGENTS) and mention logging fields or env toggles to keep DX consistent.
139
-
140
- ## Coding Style & Naming Conventions
141
- Use TypeScript with strict mode enabled and 2-space indentation. Prefer explicit return types on exported functions, `zod` schemas for all tool inputs, and descriptive FastMCP tool IDs (`hol.search`, `hol.resolveUaid`). Filenames stay kebab-case (`hashnet-tool.ts`), while classes remain PascalCase and functions camelCase. Run `pnpm lint` (ESLint + `@typescript-eslint`) and `pnpm format` (Prettier) before pushing; both respect the repo’s `.editorconfig`.
142
-
143
- ## Testing Guidelines
144
- Vitest powers unit tests, and supertest-style integration tests cover the Hono transport. Name files `*.spec.ts` for unit scope and `*.int.spec.ts` for transport flows. Mock the broker via dependency injection so tests never hit the live Registry API; only allow network calls inside smoke tests guarded by `BROKER_E2E=1`. Maintain >90% branch coverage for `src/mcp.ts` and `src/broker.ts`, since they enforce tool contracts.
145
-
146
- ## Commit & Pull Request Guidelines
147
- Follow Conventional Commits (`feat: add UAID validator`, `fix: guard vector search limit`). Bundle related file changes and keep commit bodies focused on the “why.” PRs should include: purpose summary, testing evidence (`pnpm test --run --coverage`), config diffs when touching `.env`, and screenshots or curl snippets for new transport endpoints. Link issues or TODO references in the description so MCP consumers understand the lifecycle.
148
-
149
- ## Security & Configuration Tips
150
- Never commit `.env` or keys; ship a `.env.example` with placeholder values. Treat `HEDERA_PRIVATE_KEY` as production-only and load it via secrets management when deploying. Prefer `process.env` lookups consolidated in a single `src/config.ts` module to avoid desync. When testing auto top-ups, set throttles via `Bottleneck` in `broker.ts` to keep Registry traffic predictable.
151
-
152
- ## Observability & Operations
153
- - `pino` logging is centralized in `src/logger.ts`; set `LOG_LEVEL=fatal|error|warn|info|debug|trace`.
154
- - `/healthz` replies with `{ status, uptime, tools }` for Fly/Cloud Run probes.
155
- - Enable distributed throttling by setting `BROKER_MAX_CONCURRENT`, `BROKER_MIN_TIME_MS`, and `BROKER_RATE_LIMIT_REDIS_URL`; `withBroker` automatically queues calls.
156
-
157
- ## Deployment & MCP Clients
158
- - `deploy/fly.toml` and `deploy/Dockerfile`/`deploy/README.md` document Fly.io and Cloud Run rollouts (SSE endpoint exposed at `/mcp/stream`).
159
- - Claude Desktop config: point to `pnpm dev:stdio` with the necessary Registry env vars; Claude Code/Cursor should target the SSE URL published by Fly/Cloud Run.
160
-
161
- ## NPX Installer
162
- - `npx @hol-org/hashnet-mcp up --transport sse` bootstraps deps, copies `.env.example`, and launches the desired transport; `--install-only` skips the final run.
163
- - The CLI lives in `src/cli/up.ts` (bundled to `dist/cli/up.js`) and doubles as the package `bin` for NPX installs.
package/deploy/Dockerfile DELETED
@@ -1,24 +0,0 @@
1
- FROM node:20-slim AS deps
2
- WORKDIR /app
3
- ENV NODE_ENV=production
4
- RUN corepack enable
5
- COPY package.json pnpm-lock.yaml ./
6
- RUN pnpm install --frozen-lockfile
7
-
8
- FROM node:20-slim AS builder
9
- WORKDIR /app
10
- RUN corepack enable
11
- COPY --from=deps /app/node_modules ./node_modules
12
- COPY . .
13
- RUN pnpm build
14
-
15
- FROM node:20-slim
16
- WORKDIR /app
17
- ENV NODE_ENV=production
18
- ENV PORT=3333
19
- RUN corepack enable
20
- COPY --from=builder /app/node_modules ./node_modules
21
- COPY --from=builder /app/dist ./dist
22
- COPY package.json pnpm-lock.yaml .
23
- EXPOSE 3333
24
- CMD ["node", "dist/index.js"]
package/deploy/README.md DELETED
@@ -1,31 +0,0 @@
1
- # Deployment Guide
2
-
3
- ## Fly.io
4
- 1. Install the Fly CLI and log in: `fly auth signup` or `fly auth login`.
5
- 2. Create the app and volume: `fly launch --now --copy-config --copy-env` (the bundled `fly.toml` targets `/healthz`).
6
- 3. Supply secrets:
7
- ```bash
8
- fly secrets set REGISTRY_BROKER_API_URL=... REGISTRY_BROKER_API_KEY=...
9
- fly secrets set HEDERA_ACCOUNT_ID=... HEDERA_PRIVATE_KEY=...
10
- ```
11
- 4. Deploy: `fly deploy -c deploy/fly.toml`.
12
- 5. Configure Claude Code/Cursor with `https://<app>.fly.dev/mcp/stream`.
13
-
14
- ## Cloud Run (GCP)
15
- 1. Build the container:
16
- ```bash
17
- gcloud builds submit --tag gcr.io/PROJECT_ID/hashnet-mcp --config deploy/Dockerfile
18
- ```
19
- 2. Deploy the service:
20
- ```bash
21
- gcloud run deploy hashnet-mcp \
22
- --image gcr.io/PROJECT_ID/hashnet-mcp \
23
- --platform managed \
24
- --allow-unauthenticated \
25
- --set-env-vars PORT=3333 \
26
- --set-secrets REGISTRY_BROKER_API_KEY=projects/.../secrets/... \
27
- --region us-central1
28
- ```
29
- 3. Confirm `/healthz` returns `200` and wire clients to `https://hashnet-mcp-<region>.a.run.app/mcp/stream`.
30
-
31
- Both platforms honor the same `.env` variables; enable rate limiting by setting the `BROKER_*` values and pointing Redis to a managed cache.
package/deploy/fly.toml DELETED
@@ -1,41 +0,0 @@
1
- app = "hashnet-mcp"
2
- primary_region = "iad"
3
-
4
- [build]
5
- dockerfile = "deploy/Dockerfile"
6
-
7
- [env]
8
- PORT = "3333"
9
-
10
- [[services]]
11
- processes = ["app"]
12
- protocol = "tcp"
13
- internal_port = 3333
14
-
15
- [[services.ports]]
16
- handlers = ["http"]
17
- port = 80
18
-
19
- [[services.ports]]
20
- handlers = ["tls", "http"]
21
- port = 443
22
-
23
- [[services.tcp_checks]]
24
- interval = "10s"
25
- timeout = "2s"
26
-
27
- [[services.http_checks]]
28
- interval = "15s"
29
- timeout = "5s"
30
- grace_period = "30s"
31
- method = "GET"
32
- path = "/healthz"
33
-
34
- [checks]
35
- [checks.healthz]
36
- type = "http"
37
- port = 3333
38
- interval = "15s"
39
- timeout = "5s"
40
- method = "GET"
41
- path = "/healthz"
package/dist/cli/up.js DELETED
@@ -1,175 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/cli/up.ts
4
- import { spawnSync } from "child_process";
5
- import { copyFileSync, existsSync, writeFileSync } from "fs";
6
- import path from "path";
7
- import { fileURLToPath } from "url";
8
- var dirname = path.dirname(fileURLToPath(import.meta.url));
9
- var projectRoot = path.resolve(dirname, "../..");
10
- var args = process.argv.slice(2);
11
- var command = args[0];
12
- if (!command || command === "--help" || command === "-h") {
13
- printHelp();
14
- process.exit(0);
15
- }
16
- if (command !== "up") {
17
- console.error(`Unknown command: ${command}`);
18
- printHelp();
19
- process.exit(1);
20
- }
21
- var flags = parseFlags(args.slice(1));
22
- var transport = (flags.transport ?? "stdio").toLowerCase();
23
- var installOnly = Boolean(flags["install-only"]);
24
- var preferStderr = transport === "stdio";
25
- var logInfo = (...messages) => preferStderr ? console.error(...messages) : console.log(...messages);
26
- var logWarn = (...messages) => preferStderr ? console.error(...messages) : console.warn(...messages);
27
- try {
28
- ensureNodeVersion();
29
- const packageManager = detectPackageManager(preferStderr);
30
- ensurePnpmConfig();
31
- installDependencies(packageManager, preferStderr);
32
- ensureEnvFile();
33
- if (installOnly) {
34
- logInfo("Dependencies installed. Skipping server launch (--install-only set).");
35
- process.exit(0);
36
- }
37
- runServer(packageManager, transport, preferStderr);
38
- } catch (error) {
39
- console.error(error instanceof Error ? error.message : error);
40
- process.exit(1);
41
- }
42
- function printHelp() {
43
- console.log(`Usage: npx @hol-org/hashnet-mcp up [options]
44
-
45
- Options:
46
- --transport <stdio|sse> Choose the transport to start (default: stdio)
47
- --install-only Install deps and sync .env, then exit
48
- -h, --help Show this help message`);
49
- }
50
- function parseFlags(values) {
51
- const cloned = [...values];
52
- return cloned.reduce((acc, entry, index) => {
53
- if (entry.startsWith("--")) {
54
- const [key, inlineValue] = entry.split("=");
55
- const normalized = key.replace(/^--/, "");
56
- if (inlineValue !== void 0) {
57
- acc[normalized] = inlineValue;
58
- } else {
59
- const next = cloned[index + 1];
60
- if (next && !next.startsWith("--")) {
61
- acc[normalized] = next;
62
- cloned.splice(index + 1, 1);
63
- } else {
64
- acc[normalized] = true;
65
- }
66
- }
67
- }
68
- return acc;
69
- }, {});
70
- }
71
- function ensureNodeVersion() {
72
- const major = Number(process.versions.node.split(".")[0]);
73
- if (Number.isNaN(major) || major < 18) {
74
- throw new Error(`Node.js 18+ is required (detected ${process.versions.node}).`);
75
- }
76
- }
77
- function detectPackageManager(preferStderr2) {
78
- if (commandExists("pnpm")) {
79
- return "pnpm";
80
- }
81
- logWarn("pnpm not detected. Attempting to enable via corepack...");
82
- const enabled = spawnSync("corepack", ["enable", "pnpm"], {
83
- stdio: preferStderr2 ? ["ignore", "pipe", "inherit"] : "inherit"
84
- });
85
- if (preferStderr2 && enabled.stdout?.length) {
86
- process.stderr.write(enabled.stdout);
87
- }
88
- if (enabled.status === 0 && commandExists("pnpm")) {
89
- return "pnpm";
90
- }
91
- logWarn("Falling back to npm. Install pnpm globally for faster installs.");
92
- return "npm";
93
- }
94
- function commandExists(bin) {
95
- try {
96
- const result = spawnSync(bin, ["--version"], { stdio: "ignore" });
97
- return result.status === 0;
98
- } catch {
99
- return false;
100
- }
101
- }
102
- function installDependencies(pm, preferStderr2) {
103
- logInfo(`Installing dependencies with ${pm}...`);
104
- const baseEnv = {
105
- ...process.env,
106
- NODE_OPTIONS: process.env.NODE_OPTIONS ?? "--max-old-space-size=8192"
107
- };
108
- const npmArgs = ["install", "--legacy-peer-deps"];
109
- const installResult = spawnSync(pm, pm === "pnpm" ? ["install"] : npmArgs, {
110
- cwd: projectRoot,
111
- stdio: preferStderr2 ? ["ignore", "pipe", "inherit"] : "inherit",
112
- env: baseEnv
113
- });
114
- if (preferStderr2 && installResult.stdout?.length) {
115
- process.stderr.write(installResult.stdout);
116
- }
117
- if (installResult.status !== 0) {
118
- throw new Error(`${pm} install failed.`);
119
- }
120
- }
121
- function ensurePnpmConfig() {
122
- const npmrcPath = path.join(projectRoot, ".npmrc");
123
- if (existsSync(npmrcPath)) {
124
- return;
125
- }
126
- const content = [
127
- "node-linker=isolated",
128
- "shamefully-hoist=false",
129
- "strict-peer-dependencies=false",
130
- "auto-install-peers=false",
131
- "resolution-mode=lowest-direct",
132
- "node-options=--max-old-space-size=8192",
133
- "child-concurrency=4"
134
- ].join("\n");
135
- writeFileSync(npmrcPath, content);
136
- }
137
- function ensureEnvFile() {
138
- const envPath = path.join(projectRoot, ".env");
139
- const examplePath = path.join(projectRoot, ".env.example");
140
- if (!existsSync(envPath) && existsSync(examplePath)) {
141
- copyFileSync(examplePath, envPath);
142
- logInfo("Created .env from .env.example. Remember to fill in your credentials.");
143
- }
144
- }
145
- function runServer(pm, transport2, preferStderr2) {
146
- if (!["stdio", "sse"].includes(transport2)) {
147
- throw new Error(`Unsupported transport "${transport2}". Use stdio or sse.`);
148
- }
149
- const distEntry = path.join(projectRoot, "dist", "index.js");
150
- const env = { ...process.env, MCP_TRANSPORT: transport2 };
151
- if (!existsSync(distEntry)) {
152
- logInfo("dist/index.js not found. Building project before start...");
153
- const buildResult = spawnSync(pm, ["run", "build"], {
154
- cwd: projectRoot,
155
- stdio: preferStderr2 ? ["ignore", "pipe", "inherit"] : "inherit",
156
- env
157
- });
158
- if (preferStderr2 && buildResult.stdout?.length) {
159
- process.stderr.write(buildResult.stdout);
160
- }
161
- if (buildResult.status !== 0) {
162
- throw new Error(`${pm} run build exited with code ${buildResult.status}`);
163
- }
164
- }
165
- logInfo(`Starting ${transport2} transport via ${pm} run start...`);
166
- const child = spawnSync(pm, ["run", "start"], {
167
- cwd: projectRoot,
168
- stdio: "inherit",
169
- env
170
- });
171
- if (child.status !== 0) {
172
- throw new Error(`${pm} run start exited with code ${child.status}`);
173
- }
174
- }
175
- //# sourceMappingURL=up.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/cli/up.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { copyFileSync, existsSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst dirname = path.dirname(fileURLToPath(import.meta.url));\nconst projectRoot = path.resolve(dirname, '../..');\n\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nif (!command || command === '--help' || command === '-h') {\n printHelp();\n process.exit(0);\n}\n\nif (command !== 'up') {\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n}\n\nconst flags = parseFlags(args.slice(1));\nconst transport = (flags.transport ?? 'stdio').toLowerCase();\nconst installOnly = Boolean(flags['install-only']);\nconst preferStderr = transport === 'stdio';\nconst logInfo = (...messages: any[]) => (preferStderr ? console.error(...messages) : console.log(...messages));\nconst logWarn = (...messages: any[]) => (preferStderr ? console.error(...messages) : console.warn(...messages));\n\ntry {\n ensureNodeVersion();\n const packageManager = detectPackageManager(preferStderr);\n ensurePnpmConfig();\n installDependencies(packageManager, preferStderr);\n ensureEnvFile();\n\n if (installOnly) {\n logInfo('Dependencies installed. Skipping server launch (--install-only set).');\n process.exit(0);\n }\n\n runServer(packageManager, transport, preferStderr);\n} catch (error) {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n}\n\nfunction printHelp() {\n console.log(`Usage: npx @hol-org/hashnet-mcp up [options]\\n\\nOptions:\\n --transport <stdio|sse> Choose the transport to start (default: stdio)\\n --install-only Install deps and sync .env, then exit\\n -h, --help Show this help message`);\n}\n\nfunction parseFlags(values: string[]) {\n const cloned = [...values];\n return cloned.reduce<Record<string, any>>((acc, entry, index) => {\n if (entry.startsWith('--')) {\n const [key, inlineValue] = entry.split('=');\n const normalized = key.replace(/^--/, '');\n if (inlineValue !== undefined) {\n acc[normalized] = inlineValue;\n } else {\n const next = cloned[index + 1];\n if (next && !next.startsWith('--')) {\n acc[normalized] = next;\n cloned.splice(index + 1, 1);\n } else {\n acc[normalized] = true;\n }\n }\n }\n return acc;\n }, {});\n}\n\nfunction ensureNodeVersion() {\n const major = Number(process.versions.node.split('.')[0]);\n if (Number.isNaN(major) || major < 18) {\n throw new Error(`Node.js 18+ is required (detected ${process.versions.node}).`);\n }\n}\n\nfunction detectPackageManager(preferStderr: boolean): 'pnpm' | 'npm' {\n if (commandExists('pnpm')) {\n return 'pnpm';\n }\n\n logWarn('pnpm not detected. Attempting to enable via corepack...');\n const enabled = spawnSync('corepack', ['enable', 'pnpm'], {\n stdio: preferStderr ? ['ignore', 'pipe', 'inherit'] : 'inherit',\n });\n if (preferStderr && enabled.stdout?.length) {\n process.stderr.write(enabled.stdout);\n }\n if (enabled.status === 0 && commandExists('pnpm')) {\n return 'pnpm';\n }\n\n logWarn('Falling back to npm. Install pnpm globally for faster installs.');\n return 'npm';\n}\n\nfunction commandExists(bin: string) {\n try {\n const result = spawnSync(bin, ['--version'], { stdio: 'ignore' });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n\nfunction installDependencies(pm: 'pnpm' | 'npm', preferStderr: boolean) {\n logInfo(`Installing dependencies with ${pm}...`);\n const baseEnv = {\n ...process.env,\n NODE_OPTIONS: process.env.NODE_OPTIONS ?? '--max-old-space-size=8192',\n };\n\n const npmArgs = ['install', '--legacy-peer-deps'];\n\n const installResult = spawnSync(pm, pm === 'pnpm' ? ['install'] : npmArgs, {\n cwd: projectRoot,\n stdio: preferStderr ? ['ignore', 'pipe', 'inherit'] : 'inherit',\n env: baseEnv,\n });\n if (preferStderr && installResult.stdout?.length) {\n process.stderr.write(installResult.stdout);\n }\n if (installResult.status !== 0) {\n throw new Error(`${pm} install failed.`);\n }\n}\n\nfunction ensurePnpmConfig() {\n const npmrcPath = path.join(projectRoot, '.npmrc');\n if (existsSync(npmrcPath)) {\n return;\n }\n\n const content = [\n 'node-linker=isolated',\n 'shamefully-hoist=false',\n 'strict-peer-dependencies=false',\n 'auto-install-peers=false',\n 'resolution-mode=lowest-direct',\n 'node-options=--max-old-space-size=8192',\n 'child-concurrency=4',\n ].join('\\n');\n\n writeFileSync(npmrcPath, content);\n}\n\nfunction ensureEnvFile() {\n const envPath = path.join(projectRoot, '.env');\n const examplePath = path.join(projectRoot, '.env.example');\n if (!existsSync(envPath) && existsSync(examplePath)) {\n copyFileSync(examplePath, envPath);\n logInfo('Created .env from .env.example. Remember to fill in your credentials.');\n }\n}\n\nfunction runServer(pm: 'pnpm' | 'npm', transport: string, preferStderr: boolean) {\n if (!['stdio', 'sse'].includes(transport)) {\n throw new Error(`Unsupported transport \"${transport}\". Use stdio or sse.`);\n }\n const distEntry = path.join(projectRoot, 'dist', 'index.js');\n const env = { ...process.env, MCP_TRANSPORT: transport };\n\n if (!existsSync(distEntry)) {\n logInfo('dist/index.js not found. Building project before start...');\n const buildResult = spawnSync(pm, ['run', 'build'], {\n cwd: projectRoot,\n stdio: preferStderr ? ['ignore', 'pipe', 'inherit'] : 'inherit',\n env,\n });\n if (preferStderr && buildResult.stdout?.length) {\n process.stderr.write(buildResult.stdout);\n }\n if (buildResult.status !== 0) {\n throw new Error(`${pm} run build exited with code ${buildResult.status}`);\n }\n }\n\n logInfo(`Starting ${transport} transport via ${pm} run start...`);\n const child = spawnSync(pm, ['run', 'start'], {\n cwd: projectRoot,\n stdio: 'inherit',\n env,\n });\n if (child.status !== 0) {\n throw new Error(`${pm} run start exited with code ${child.status}`);\n }\n}\n"],"mappings":"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","names":["preferStderr","transport"]}