@hol-org/hashnet-mcp 1.0.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/AGENTS.md +133 -0
- package/README.md +251 -0
- package/deploy/Dockerfile +24 -0
- package/deploy/README.md +31 -0
- package/deploy/fly.toml +41 -0
- package/dist/cli/up.js +128 -0
- package/dist/cli/up.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1946 -0
- package/dist/index.js.map +1 -0
- package/examples/agent-registration-request.json +48 -0
- package/examples/workflows/workflow.agentverseBridge.json +15 -0
- package/examples/workflows/workflow.chatSmoke.json +8 -0
- package/examples/workflows/workflow.discovery.json +4 -0
- package/examples/workflows/workflow.erc8004Discovery.json +4 -0
- package/examples/workflows/workflow.erc8004X402.json +87 -0
- package/examples/workflows/workflow.fullRegistration.json +73 -0
- package/examples/workflows/workflow.historyTopUp.json +14 -0
- package/examples/workflows/workflow.ledgerAuth.json +8 -0
- package/examples/workflows/workflow.openrouterChat.json +7 -0
- package/examples/workflows/workflow.opsCheck.json +3 -0
- package/examples/workflows/workflow.registerAgentAdvanced.json +84 -0
- package/examples/workflows/workflow.registerAgentErc8004.json +82 -0
- package/examples/workflows/workflow.registerMcp.json +71 -0
- package/examples/workflows/workflow.registryBrokerShowcase.json +6 -0
- package/examples/workflows/workflow.x402Registration.json +83 -0
- package/examples/workflows/workflow.x402TopUp.json +13 -0
- package/package.json +79 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
|
+
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`.
|
|
5
|
+
|
|
6
|
+
## Build, Test, and Development Commands
|
|
7
|
+
- `pnpm install` — syncs dependencies, including FastMCP, Hono, and the Hashgraph Online SDK.
|
|
8
|
+
- `pnpm dev:stdio` — launches `src/index.ts` with `MCP_TRANSPORT=stdio`, wired for Claude Desktop.
|
|
9
|
+
- `pnpm dev:sse` — serves `http://localhost:${PORT}/mcp/stream` via Hono’s SSE transport for Claude Code/Cursor.
|
|
10
|
+
- `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`.
|
|
11
|
+
- `pnpm build` — runs `tsup` (see `tsup.config.ts`) to emit both `dist/index.js` and the NPX CLI bundle.
|
|
12
|
+
- `pnpm start` — executes the compiled output (`node dist/index.js`) for production parity.
|
|
13
|
+
- `pnpm test --run --coverage` — executes Vitest once with V8 coverage; `pnpm test` stays in watch mode.
|
|
14
|
+
- `pnpm quickstart` — interactive DX script that installs deps, copies `.env`, runs smoke tests, and launches your preferred transport (stdio or SSE).
|
|
15
|
+
- `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.
|
|
16
|
+
- `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.
|
|
17
|
+
- `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).
|
|
18
|
+
- `pnpm workflow:list` / `pnpm workflow:run <name>` — inspect and execute any workflow (pair with `examples/workflows/<workflow>.json` for sample payloads).
|
|
19
|
+
- `pnpm workflow:register` — interactive wizard that runs the registration/chat/ops workflows and saves a JSON report (UAID + Claude snippet).
|
|
20
|
+
- `pnpm workflow:register:advanced` — guided prompts for `workflow.registerAgentAdvanced` (additional registries + optional credit purchase).
|
|
21
|
+
- `pnpm workflow:register:erc8004` — helper around the ERC-8004 workflow (ledger walkthrough + post-registration chat).
|
|
22
|
+
- `pnpm mock:broker` — local mock broker for CI/contract testing.
|
|
23
|
+
- 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.
|
|
24
|
+
|
|
25
|
+
## Workflows
|
|
26
|
+
`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>`.
|
|
27
|
+
|
|
28
|
+
**Discovery & Ops**
|
|
29
|
+
- `workflow.discovery` and `workflow.erc8004Discovery` (search/vector/namespace lookups).
|
|
30
|
+
- `workflow.opsCheck` (stats + metrics + protocols).
|
|
31
|
+
- `workflow.registryBrokerShowcase` (discovery → analytics → optional chat).
|
|
32
|
+
|
|
33
|
+
**Registration Pipelines**
|
|
34
|
+
- `workflow.registerMcp`, `workflow.registerAgentAdvanced`, `workflow.registerAgentErc8004`.
|
|
35
|
+
- `workflow.fullRegistration` (discovery → registration → chat → ops).
|
|
36
|
+
- `workflow.erc8004X402` and `workflow.x402Registration` (registration funded via X402 with optional chat checks).
|
|
37
|
+
|
|
38
|
+
**Credit & Ledger Utilities**
|
|
39
|
+
- `workflow.ledgerAuth` (challenge + verify).
|
|
40
|
+
- `workflow.x402TopUp` (buy credits via X402).
|
|
41
|
+
- `workflow.historyTopUp` (chat compaction + HBAR auto-purchases on 402 errors).
|
|
42
|
+
|
|
43
|
+
**Chat & Interop**
|
|
44
|
+
- `workflow.chatSmoke` (UAID session lifecycle).
|
|
45
|
+
- `workflow.openrouterChat` (discover + ping an OpenRouter model).
|
|
46
|
+
- `workflow.agentverseBridge` (relay traffic between a local UAID and Agentverse).
|
|
47
|
+
|
|
48
|
+
Runbook examples:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pnpm workflow:run workflow.registerAgentAdvanced --payload examples/workflows/workflow.registerAgentAdvanced.json
|
|
52
|
+
pnpm workflow:run workflow.openrouterChat --payload examples/workflows/workflow.openrouterChat.json --endpoint https://host/mcp/stream
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each workflow emits a structured report (steps, timings, context) whether executed via MCP or CLI.
|
|
56
|
+
|
|
57
|
+
### Workflow Architecture
|
|
58
|
+
- Pipelines live in `src/workflows/`; each file exports a `registerPipeline()` definition.
|
|
59
|
+
- `src/workflows/index.ts` imports these modules so they register at startup.
|
|
60
|
+
- MCP tools (`workflow.*`) are wired in `src/mcp.ts` and simply call the pipelines.
|
|
61
|
+
- For a visual overview, see the "Architecture Overview" section in `README.md`.
|
|
62
|
+
|
|
63
|
+
### Required Environment Variables
|
|
64
|
+
- `REGISTRY_BROKER_API_URL` / `REGISTRY_BROKER_API_KEY` — broker endpoint + key (staging/testnet supported).
|
|
65
|
+
- `HEDERA_ACCOUNT_ID` / `HEDERA_PRIVATE_KEY` — optional; only set these if you want CLI-driven credit purchases when the key runs dry.
|
|
66
|
+
- `WORKFLOW_DRY_RUN=1` — optional guard that makes pipelines skip state-changing broker calls.
|
|
67
|
+
- `BROKER_E2E=1` — opt into real broker hits in CI; otherwise the mock broker is used when available.
|
|
68
|
+
- `BROKER_AUTO_TOP_UP=1` — opt into automatic broker purchases without HITL prompts (defaults to manual approvals).
|
|
69
|
+
- X402 workflows also require EVM wallet details (see `examples/workflows/workflow.x402*.json`) plus any ledger challenge metadata referenced in the payload.
|
|
70
|
+
- 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.
|
|
71
|
+
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.
|
|
72
|
+
|
|
73
|
+
### Running `pnpm workflow:register`
|
|
74
|
+
1. The CLI prompts for display name, alias, description, MCP URL, chat message, and report path (defaults provided).
|
|
75
|
+
2. Runs the registration → chat → ops pipelines and prints progress to the console.
|
|
76
|
+
3. Writes the report JSON (defaults to `workflow-register-report.json`) containing:
|
|
77
|
+
- UAID (if registration succeeded)
|
|
78
|
+
- Pipeline traces (steps + context)
|
|
79
|
+
- Claude config snippet pointing at your MCP URL
|
|
80
|
+
- Raw pipeline responses for registration/chat/ops
|
|
81
|
+
|
|
82
|
+
**Sample report excerpt**
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"uaid": "uaid:registry:abcd-1234",
|
|
87
|
+
"pipelines": [
|
|
88
|
+
{ "name": "workflow.registerMcp", "steps": ["hol.getRegistrationQuote", "hol.registerAgent"] }
|
|
89
|
+
],
|
|
90
|
+
"claudeConfig": {
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"hashnet": {
|
|
93
|
+
"command": "npx",
|
|
94
|
+
"args": ["@hol-org/hashnet-mcp@latest", "up", "--transport", "sse"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
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.
|
|
102
|
+
|
|
103
|
+
### Adding / Extending Workflows
|
|
104
|
+
1. Create `src/workflows/<name>.ts`, register the pipeline with metadata + env requirements, and export any helper schemas.
|
|
105
|
+
2. Import it from `src/workflows/index.ts` so registration happens at server boot.
|
|
106
|
+
3. Optional MCP tool: define schemas/handlers in `src/mcp.ts` and wrap responses with `formatPipelineResult`.
|
|
107
|
+
4. Tests belong in `tests/workflows/<name>.spec.ts` (unit) plus CLI hooks in `scripts/workflow-run*.ts` when interactive flags are required.
|
|
108
|
+
5. Document new workflows (README/AGENTS) and mention logging fields or env toggles to keep DX consistent.
|
|
109
|
+
|
|
110
|
+
## Coding Style & Naming Conventions
|
|
111
|
+
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`.
|
|
112
|
+
|
|
113
|
+
## Testing Guidelines
|
|
114
|
+
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.
|
|
115
|
+
|
|
116
|
+
## Commit & Pull Request Guidelines
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
## Security & Configuration Tips
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
## Observability & Operations
|
|
123
|
+
- `pino` logging is centralized in `src/logger.ts`; set `LOG_LEVEL=fatal|error|warn|info|debug|trace`.
|
|
124
|
+
- `/healthz` replies with `{ status, uptime, tools }` for Fly/Cloud Run probes.
|
|
125
|
+
- Enable distributed throttling by setting `BROKER_MAX_CONCURRENT`, `BROKER_MIN_TIME_MS`, and `BROKER_RATE_LIMIT_REDIS_URL`; `withBroker` automatically queues calls.
|
|
126
|
+
|
|
127
|
+
## Deployment & MCP Clients
|
|
128
|
+
- `deploy/fly.toml` and `deploy/Dockerfile`/`deploy/README.md` document Fly.io and Cloud Run rollouts (SSE endpoint exposed at `/mcp/stream`).
|
|
129
|
+
- 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.
|
|
130
|
+
|
|
131
|
+
## NPX Installer
|
|
132
|
+
- `npx @hol-org/hashnet-mcp up --transport sse` bootstraps deps, copies `.env.example`, and launches the desired transport; `--install-only` skips the final run.
|
|
133
|
+
- The CLI lives in `src/cli/up.ts` (bundled to `dist/cli/up.js`) and doubles as the package `bin` for NPX installs.
|
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Hashnet MCP
|
|
2
|
+
|
|
3
|
+
Node/TypeScript Model Context Protocol server exposing the Hashgraph Online Registry Broker via FastMCP, with both stdio and SSE transports. See `AGENTS.md` for full contributor guidance.
|
|
4
|
+
|
|
5
|
+
## Directory Layout
|
|
6
|
+
- `src/` – application source code. Core entrypoints (`mcp.ts`, `broker.ts`) live here.
|
|
7
|
+
- `src/schemas/` – shared zod schemas and SDK-driven types.
|
|
8
|
+
- `src/transports/` – stdio/SSE transport helpers plus bootstrap glue.
|
|
9
|
+
- `tests/` – Vitest specs mirroring the `src/` tree.
|
|
10
|
+
- `examples/` – sample payloads, env snippets, and integration fixtures.
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
```
|
|
14
|
+
pnpm install
|
|
15
|
+
pnpm dev:stdio # once scripts are added
|
|
16
|
+
```
|
|
17
|
+
Copy `.env.example` to `.env` and fill in Registry Broker + Hedera credentials before running transports.
|
|
18
|
+
All transports, workflows, and CLI scripts load `.env` automatically via `dotenv`, so once the file is populated you can run `pnpm dev:*`, `pnpm workflow:*`, or tests without re-exporting environment variables.
|
|
19
|
+
|
|
20
|
+
### Zero-touch Quickstart
|
|
21
|
+
```
|
|
22
|
+
pnpm quickstart
|
|
23
|
+
```
|
|
24
|
+
The DX script will:
|
|
25
|
+
1. Copy `.env.example` (and let you inject your Registry Broker API key).
|
|
26
|
+
2. Install dependencies, build the project, and run smoke tests.
|
|
27
|
+
3. Launch the dev transport you choose (`sse` by default) with a stylized CLI experience.
|
|
28
|
+
|
|
29
|
+
## Logging & Health
|
|
30
|
+
- `LOG_LEVEL` controls `pino` verbosity (`fatal`, `error`, `warn`, `info`, `debug`, `trace`).
|
|
31
|
+
- Every tool invocation is logged with a requestId plus duration; SSE/stdio transport logs each HTTP request.
|
|
32
|
+
- `/healthz` returns `{ status, uptime, tools }` so Fly.io/Cloud Run probes can verify readiness.
|
|
33
|
+
- Optional rate limiting is enabled via the `BROKER_*` variables; point `BROKER_RATE_LIMIT_REDIS_URL` at your Redis cluster to queue requests across replicas.
|
|
34
|
+
- `HTTP_STREAM_PORT` lets you pin the internal FastMCP HTTP-stream backend to a specific port (defaults to `PORT + 1`). External clients always connect to `http://<host>:PORT/mcp/stream` (Streamable HTTP) or `http://<host>:PORT/mcp/sse` (SSE fallback).
|
|
35
|
+
|
|
36
|
+
## MCP Client Configuration
|
|
37
|
+
Claude Desktop (stdio):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"hashgraph-standards": {
|
|
43
|
+
"command": "pnpm",
|
|
44
|
+
"args": ["dev:stdio"],
|
|
45
|
+
"env": {
|
|
46
|
+
"REGISTRY_BROKER_API_URL": "https://registry.hashgraphonline.com/api/v1",
|
|
47
|
+
"REGISTRY_BROKER_API_KEY": "YOUR_KEY"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Claude Code / Cursor (HTTP streaming): point the client at `https://<host>/mcp/stream` once deployed (see `deploy/README.md`). Use `pnpm dev:sse` locally to expose the same endpoint at `http://localhost:3333/mcp/stream`. SSE fallback remains available at `/mcp/sse` for clients that still require it.
|
|
55
|
+
|
|
56
|
+
#### Claude Code auto-install
|
|
57
|
+
- `pnpm claude:install --endpoint http://localhost:3333/mcp/stream` — adds/updates the MCP entry inside Claude Code’s config (`~/Library/Application Support/Claude/claude_code_config.json` on macOS; `%APPDATA%\Claude\claude_code_config.json` on Windows; `~/.config/Claude/claude_code_config.json` on Linux).
|
|
58
|
+
- Pass `--name <id>` to customize the MCP server handle or `--config <path>` if your Claude Code build stores config elsewhere.
|
|
59
|
+
- Use `--force` to overwrite an existing entry, `--dry-run` to preview changes, and `--skip-backup` to avoid writing a `.bak` file next to the config.
|
|
60
|
+
- After running the script, restart Claude Code so the client discovers the new `hol.*` tool catalog automatically.
|
|
61
|
+
|
|
62
|
+
#### Cursor auto-install
|
|
63
|
+
- `pnpm cursor:install --endpoint http://localhost:3333/mcp/stream` — writes/updates the `modelContextProtocol.servers` array inside Cursor’s `settings.json` (`~/Library/Application Support/Cursor/User/settings.json` on macOS; `%APPDATA%\Cursor\User\settings.json` on Windows; `~/.config/Cursor/User/settings.json` on Linux).
|
|
64
|
+
- Use `--name <id>` to change the MCP handle, `--config <path>` to point at a custom settings file, and `--force` if you need to replace an existing server entry.
|
|
65
|
+
- Add `--dry-run` to preview the merged JSON. Once applied, restart Cursor so the MCP list refreshes and exposes the `hol.*` commands.
|
|
66
|
+
|
|
67
|
+
### Workflow Helpers
|
|
68
|
+
Run `pnpm workflow:list` to see the live catalog (the script reads `src/workflows/index.ts`, so it never goes stale). Each workflow’s golden-path payload lives under `examples/workflows/`—copy one, replace the placeholder UAIDs/keys, and pass it to `pnpm workflow:run <workflow> --payload <file>`.
|
|
69
|
+
|
|
70
|
+
**Discovery & Ops**
|
|
71
|
+
- `workflow.discovery` – `hol.search` + `hol.vectorSearch` (`examples/workflows/workflow.discovery.json`)
|
|
72
|
+
- `workflow.erc8004Discovery` – ERC-8004 search + namespace lookup (`examples/workflows/workflow.erc8004Discovery.json`)
|
|
73
|
+
- `workflow.opsCheck` – Stats/metrics/protocol snapshot (`examples/workflows/workflow.opsCheck.json`)
|
|
74
|
+
- `workflow.registryBrokerShowcase` – Discovery + analytics + optional chat (`examples/workflows/workflow.registryBrokerShowcase.json`)
|
|
75
|
+
|
|
76
|
+
**Registration Pipelines**
|
|
77
|
+
- `workflow.registerMcp` – Quote → register → wait (`examples/workflows/workflow.registerMcp.json`)
|
|
78
|
+
- `workflow.registerAgentAdvanced` – Additional registries + optional update + HITL credit top-up (`examples/workflows/workflow.registerAgentAdvanced.json`)
|
|
79
|
+
- `workflow.registerAgentErc8004` – ERC-8004 network resolution + optional ledger verification (`examples/workflows/workflow.registerAgentErc8004.json`)
|
|
80
|
+
- `workflow.erc8004X402` – ERC-8004 registration funded via X402 with follow-up chat (`examples/workflows/workflow.erc8004X402.json`)
|
|
81
|
+
- `workflow.x402Registration` – General-purpose registration paid for by X402 credits (`examples/workflows/workflow.x402Registration.json`)
|
|
82
|
+
- `workflow.fullRegistration` – Discovery → registration → chat → ops composite (`examples/workflows/workflow.fullRegistration.json`)
|
|
83
|
+
|
|
84
|
+
**Credit & Ledger Utilities**
|
|
85
|
+
- `workflow.ledgerAuth` – Create + verify ledger challenges (`examples/workflows/workflow.ledgerAuth.json`)
|
|
86
|
+
- `workflow.x402TopUp` – Buy credits via X402 (`examples/workflows/workflow.x402TopUp.json`)
|
|
87
|
+
- `workflow.historyTopUp` – Chat + history compaction + automatic HBAR purchases on 402s (`examples/workflows/workflow.historyTopUp.json`)
|
|
88
|
+
|
|
89
|
+
**Chat & Interop**
|
|
90
|
+
- `workflow.chatSmoke` – Session lifecycle validation (`examples/workflows/workflow.chatSmoke.json`)
|
|
91
|
+
- `workflow.openrouterChat` – Discover an OpenRouter UAID and send an authenticated message (`examples/workflows/workflow.openrouterChat.json`)
|
|
92
|
+
- `workflow.agentverseBridge` – Relay between a local UAID and Agentverse (`examples/workflows/workflow.agentverseBridge.json`)
|
|
93
|
+
|
|
94
|
+
Common CLI patterns:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
pnpm workflow:list
|
|
98
|
+
pnpm workflow:run workflow.registerAgentAdvanced --payload examples/workflows/workflow.registerAgentAdvanced.json
|
|
99
|
+
pnpm workflow:run workflow.openrouterChat --payload examples/workflows/workflow.openrouterChat.json --endpoint https://host/mcp/stream
|
|
100
|
+
pnpm workflow:register
|
|
101
|
+
pnpm workflow:register:advanced
|
|
102
|
+
pnpm workflow:register:erc8004
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`workflow:run` spawns the local server (if needed) and prints a structured pipeline report. Use `--endpoint https://host/mcp/stream` to target a remote deployment; add `--reuse-server` to skip spawning when pointing at an already-running localhost instance.
|
|
106
|
+
|
|
107
|
+
`pnpm workflow:register` flow:
|
|
108
|
+
1. CLI prompts for display name, alias, description, MCP URL, chat message, and report path.
|
|
109
|
+
2. Runs `workflow.registerMcp`, `workflow.chatSmoke`, `workflow.opsCheck` sequentially.
|
|
110
|
+
3. Saves `workflow-register-report.json` (configurable) containing:
|
|
111
|
+
- `uaid`
|
|
112
|
+
- Pipeline traces (steps, dry-run flag, UAID context)
|
|
113
|
+
- Claude config snippet with your MCP URL
|
|
114
|
+
- Raw pipeline results (registration/chat/ops)
|
|
115
|
+
|
|
116
|
+
**Sample report snippet**
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"uaid": "uaid:registry:abcd-1234",
|
|
121
|
+
"claudeConfig": {
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"hashnet": {
|
|
124
|
+
"command": "npx",
|
|
125
|
+
"args": ["@hol-org/hashnet-mcp@latest", "up"]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"pipelines": [
|
|
130
|
+
{
|
|
131
|
+
"name": "workflow.registerMcp",
|
|
132
|
+
"steps": [
|
|
133
|
+
{ "id": "hol.getRegistrationQuote", "durationMs": 812 },
|
|
134
|
+
{ "id": "hol.registerAgent", "durationMs": 1420 }
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
During `workflow.registerMcp`, the CLI always attempts to spend the API key’s existing credits first. Only if the broker rejects the registration with a 402 does the script fetch the latest quote, show the required/available credits plus the estimated ℏ charge, and ask for HITL approval before calling `purchaseCreditsWithHbar`. It then polls the quote endpoint until the newly purchased credits appear so you never pay without seeing the balance update. Set `BROKER_AUTO_TOP_UP=1` if you need the “fire-and-forget” auto top-up behaviour with no prompts.
|
|
142
|
+
|
|
143
|
+
#### Workflow Environment Requirements
|
|
144
|
+
- Every workflow requires `REGISTRY_BROKER_API_KEY`. Discovery/ops flows can run with just that variable.
|
|
145
|
+
- Registration workflows (`workflow.register*`, `workflow.fullRegistration`) will prompt for purchases when credits are low. Provide `HEDERA_ACCOUNT_ID` + `HEDERA_PRIVATE_KEY` to allow automatic HBAR-based top-ups; otherwise the pipeline aborts on 402.
|
|
146
|
+
- X402 flows (`workflow.x402*`, `workflow.erc8004X402`) also need an EVM private key plus any ledger verification payloads referenced in the sample JSON.
|
|
147
|
+
- Chat/interop flows (`workflow.chatSmoke`, `workflow.agentverseBridge`, `workflow.openrouterChat`, `workflow.historyTopUp`) require valid UAIDs and, when applicable, bearer tokens for the downstream service.
|
|
148
|
+
- `examples/workflows/*.json` files include clearly named placeholders (e.g., `0xYOUR_EVM_PRIVATE_KEY`). Replace them before running `workflow:run`; keeping them as-is will cause schema validation or broker auth failures.
|
|
149
|
+
|
|
150
|
+
### Hol Tool Catalog
|
|
151
|
+
These FastMCP tools are available everywhere the server runs (CLI, Claude Desktop, Claude Code, Cursor, or any MCP-compliant client).
|
|
152
|
+
|
|
153
|
+
| Tool | Purpose |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `hol.search.help` | Markdown primer that documents filters and usage for search-heavy agents. |
|
|
156
|
+
| `hol.search` | Keyword search across the Registry Broker catalog with filtering and sorting controls. |
|
|
157
|
+
| `hol.vectorSearch` | Embedding-backed discovery endpoint for semantic matches. |
|
|
158
|
+
| `hol.resolveUaid` | Look up metadata and trust status for a UAID or alias. |
|
|
159
|
+
| `hol.closeUaidConnection` | Forcibly closes any active UAID tunnel after ops or chat completes. |
|
|
160
|
+
| `hol.getRegistrationQuote` | Fetch cost + prerequisites before registering a new agent. |
|
|
161
|
+
| `hol.registerAgent` | Submit a signed registration payload to the broker. |
|
|
162
|
+
| `hol.waitForRegistrationCompletion` | Poll the broker until a registration attempt succeeds, fails, or times out. |
|
|
163
|
+
| `hol.chat.createSession` | Start a chat session scoped to a UAID. |
|
|
164
|
+
| `hol.chat.sendMessage` | Send a broker-routed chat message within an existing session. |
|
|
165
|
+
| `hol.chat.history` | Retrieve the message transcript for observability or resume flows. |
|
|
166
|
+
| `hol.chat.compact` | Truncate chat history while preserving a fixed number of recent entries. |
|
|
167
|
+
| `hol.chat.end` | End the active session and release broker-side resources. |
|
|
168
|
+
| `hol.listProtocols` | Enumerate known Registry Broker protocols plus transport metadata. |
|
|
169
|
+
| `hol.detectProtocol` | Inspect headers/body to auto-detect which broker protocol applies. |
|
|
170
|
+
| `hol.stats` | Aggregate registry stats (totals, growth, health). |
|
|
171
|
+
| `hol.metricsSummary` | Pull summarized broker metrics for dashboards. |
|
|
172
|
+
| `hol.dashboardStats` | Fetch the dashboard-friendly stats bundle used by ops workflows. |
|
|
173
|
+
| `hol.credits.balance` | Return API key, Hedera, and (optionally) X402 credit balances from `/credits/balance`. |
|
|
174
|
+
|
|
175
|
+
#### Invoking Tools from the CLI
|
|
176
|
+
- `pnpm test:tools` — boot the SSE transport on `http://localhost:3333/mcp/stream` and run every `hol.*` tool with sample payloads (set `TEST_UAID`, `TEST_CHAT_UAID`, `TEST_REGISTRATION_ATTEMPT_ID` so UAID/chat steps execute instead of skipping). No need to append `--spawn`; the script already starts/stops the local server.
|
|
177
|
+
- `pnpm test:tools:mock` — smoke-test the CLI against the bundled mock MCP server (no registry/Broker access required). Useful for CI to verify the harness exits cleanly with code 0.
|
|
178
|
+
- `pnpm test:tools --endpoint https://host/mcp/stream` — hit a remote deployment without spawning a local server; combine with `--port <n>` when using `--spawn` to change the local port.
|
|
179
|
+
- Use `TEST_UAID=<uaid> TEST_CHAT_UAID=<uaid> pnpm test:tools --spawn` to pass environment variables inline; the harness reports missing vars and skips dependent tools if they are not provided.
|
|
180
|
+
- Set `BROKER_PROTOCOL_TOOLS=1` if your API key has access to the `/protocols` + `/detect-protocol` endpoints; otherwise those scenarios are skipped (the staging broker currently returns 404 for those routes).
|
|
181
|
+
- For ad-hoc calls, point `scripts/run-tool-suite.ts` at any MCP endpoint (`tsx scripts/run-tool-suite.ts --endpoint ...`) and edit the scenarios array to focus on a subset of tools or custom payloads.
|
|
182
|
+
|
|
183
|
+
### Workflow Environment Requirements
|
|
184
|
+
- `REGISTRY_BROKER_API_URL` / `REGISTRY_BROKER_API_KEY` – base URL + key for the Registry Broker API (staging/testnet supported).
|
|
185
|
+
- `HEDERA_ACCOUNT_ID` / `HEDERA_PRIVATE_KEY` – optional; only required if you want the CLI to purchase credits on your behalf when the API key runs dry.
|
|
186
|
+
- `WORKFLOW_DRY_RUN=1` – skip state-changing steps (quote-only, no registration).
|
|
187
|
+
- `BROKER_E2E=1` – opt in to real broker calls inside CI/e2e scripts (otherwise the mock broker is used where possible).
|
|
188
|
+
- `BROKER_AUTO_TOP_UP=1` – opt in to automatic broker purchases without prompts (defaults to `0`, which enables HITL confirmation).
|
|
189
|
+
- Tool-suite fixtures (optional): `TEST_UAID`, `TEST_CHAT_UAID`, `TEST_REGISTRATION_ATTEMPT_ID`, and `BROKER_PROTOCOL_TOOLS`. Populate these in `.env` (see `.env.example`) so `pnpm test:tools` can run UAID/chat flows locally and skip protocol checks unless your API key has access. Leave them blank to let the suite auto-discover UAIDs/attempt IDs from preceding steps.
|
|
190
|
+
|
|
191
|
+
Each workflow definition lists its required env vars and the pipeline runner will fail fast (before touching the broker) if any are missing. Restart the MCP server after changing credentials so FastMCP snapshots the updated environment.
|
|
192
|
+
|
|
193
|
+
## NPX Quick Start
|
|
194
|
+
```
|
|
195
|
+
npx @hol-org/hashnet-mcp up --transport sse
|
|
196
|
+
```
|
|
197
|
+
The CLI verifies Node ≥18, installs dependencies (preferring pnpm), copies `.env.example` if needed, and launches the requested transport. Pass `--install-only` to skip auto-start. See `AGENTS.md` for advanced flags.
|
|
198
|
+
|
|
199
|
+
## Deployment
|
|
200
|
+
- `deploy/fly.toml` – Fly.io app manifest exposing `/mcp/stream`.
|
|
201
|
+
- `deploy/Dockerfile` – Cloud Run-ready image that builds the project and runs `node dist/index.js`.
|
|
202
|
+
- `deploy/README.md` – step-by-step instructions for both targets.
|
|
203
|
+
|
|
204
|
+
## Architecture Overview
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
[ CLI / Agent ]
|
|
208
|
+
│
|
|
209
|
+
▼
|
|
210
|
+
┌──────────────────────┐
|
|
211
|
+
│ MCP Gateway (Hono) │
|
|
212
|
+
│ /mcp/stream & /mcp/sse│
|
|
213
|
+
└─────────┬────────────┘
|
|
214
|
+
│ proxies HTTP stream traffic
|
|
215
|
+
┌─────────▼────────────┐
|
|
216
|
+
│ FastMCP / mcp.ts │ ← registers `hol.*` + `workflow.*` tools
|
|
217
|
+
│ ├─ broker.ts │ ← wraps RegistryBrokerClient
|
|
218
|
+
│ └─ workflows/ │ ← pipeline definitions + registry
|
|
219
|
+
└─────────┬────────────┘
|
|
220
|
+
│
|
|
221
|
+
▼
|
|
222
|
+
┌──────────────────────┐
|
|
223
|
+
│ Workflow Pipelines │
|
|
224
|
+
│ (discovery/register/ │
|
|
225
|
+
│ chat/ops/full) │
|
|
226
|
+
└──────────────────────┘
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- `src/workflows/pipeline.ts` implements the reusable pipeline engine (steps, hooks, dry-run).
|
|
230
|
+
- `src/workflows/*.ts` define domain workflows; importing `src/workflows/index.ts` registers them.
|
|
231
|
+
- MCP tools (`workflow.*`) simply call the registered pipelines and return structured reports.
|
|
232
|
+
|
|
233
|
+
## Examples
|
|
234
|
+
- `examples/agent-registration-request.json` mirrors the stricter schema used by `hol.registerAgent` and `hol.getRegistrationQuote`.
|
|
235
|
+
|
|
236
|
+
## Testing & Automation
|
|
237
|
+
- `pnpm test --run --coverage` — runs Vitest in CI mode with V8 coverage, ensuring `src/mcp.ts` and `src/broker.ts` stay above the 90% branch threshold.
|
|
238
|
+
- `pnpm test:run` — quick single-pass test run without coverage.
|
|
239
|
+
- `pnpm test:tools` — spins up the HTTP-stream gateway (unless one is already running) and exercises every MCP tool end-to-end via the official MCP client (Streamable HTTP transport). Set `TEST_UAID`, `TEST_CHAT_UAID`, and `TEST_REGISTRATION_ATTEMPT_ID` if you want UAID-specific flows to run instead of being skipped.
|
|
240
|
+
- `pnpm workflow:list` / `pnpm workflow:run <name>` — inspect and execute the built-in pipelines (use `examples/workflows/*.json` for payloads).
|
|
241
|
+
- `pnpm workflow:register` — prompts for metadata, runs the registration/chat/ops pipelines, and writes a JSON report (UAID, Claude config snippet, workflow traces).
|
|
242
|
+
- `pnpm workflow:register:advanced` — guided version of the advanced workflow (additional registries + optional credit purchasing prompts).
|
|
243
|
+
- `pnpm workflow:register:erc8004` — helper around the ERC-8004 workflow (ledger prompts + X402/X-Ledger guidance).
|
|
244
|
+
- `pnpm mock:broker` — boot a lightweight mock Registry Broker for CI/testing without external dependencies.
|
|
245
|
+
|
|
246
|
+
## Adding New Workflows
|
|
247
|
+
1. Create `src/workflows/<name>.ts` exporting a factory that calls `registerPipeline()` with metadata (description, input schema, required env vars).
|
|
248
|
+
2. Import the module inside `src/workflows/index.ts` so the pipeline registers at startup.
|
|
249
|
+
3. Wire an MCP tool in `src/mcp.ts` (define a `zod` schema, call `runPipeline`, and convert the result with `formatPipelineResult`).
|
|
250
|
+
4. Add Vitest coverage in `tests/workflows/<name>.spec.ts` plus CLI smoke coverage if pipelines should be runnable via `workflow:run`.
|
|
251
|
+
5. Update README/AGENTS describing the workflow, logging fields, and any CLI flags so downstream agents know how to trigger it.
|
|
@@ -0,0 +1,24 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/cli/up.ts
|
|
5
|
+
import { spawnSync } from "child_process";
|
|
6
|
+
import { copyFileSync, existsSync } from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
var dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
var projectRoot = path.resolve(dirname, "../..");
|
|
11
|
+
var args = process.argv.slice(2);
|
|
12
|
+
var command = args[0];
|
|
13
|
+
if (!command || command === "--help" || command === "-h") {
|
|
14
|
+
printHelp();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
if (command !== "up") {
|
|
18
|
+
console.error(`Unknown command: ${command}`);
|
|
19
|
+
printHelp();
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
var flags = parseFlags(args.slice(1));
|
|
23
|
+
var transport = (flags.transport ?? "stdio").toLowerCase();
|
|
24
|
+
var installOnly = Boolean(flags["install-only"]);
|
|
25
|
+
try {
|
|
26
|
+
ensureNodeVersion();
|
|
27
|
+
const packageManager = detectPackageManager();
|
|
28
|
+
installDependencies(packageManager);
|
|
29
|
+
ensureEnvFile();
|
|
30
|
+
if (installOnly) {
|
|
31
|
+
console.log("Dependencies installed. Skipping server launch (--install-only set).");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
runServer(packageManager, transport);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(error instanceof Error ? error.message : error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
function printHelp() {
|
|
40
|
+
console.log(`Usage: npx @hol-org/hashnet-mcp up [options]
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
--transport <stdio|sse> Choose the transport to start (default: stdio)
|
|
44
|
+
--install-only Install deps and sync .env, then exit
|
|
45
|
+
-h, --help Show this help message`);
|
|
46
|
+
}
|
|
47
|
+
function parseFlags(values) {
|
|
48
|
+
const cloned = [...values];
|
|
49
|
+
return cloned.reduce((acc, entry, index) => {
|
|
50
|
+
if (entry.startsWith("--")) {
|
|
51
|
+
const [key, inlineValue] = entry.split("=");
|
|
52
|
+
const normalized = key.replace(/^--/, "");
|
|
53
|
+
if (inlineValue !== void 0) {
|
|
54
|
+
acc[normalized] = inlineValue;
|
|
55
|
+
} else {
|
|
56
|
+
const next = cloned[index + 1];
|
|
57
|
+
if (next && !next.startsWith("--")) {
|
|
58
|
+
acc[normalized] = next;
|
|
59
|
+
cloned.splice(index + 1, 1);
|
|
60
|
+
} else {
|
|
61
|
+
acc[normalized] = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return acc;
|
|
66
|
+
}, {});
|
|
67
|
+
}
|
|
68
|
+
function ensureNodeVersion() {
|
|
69
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
70
|
+
if (Number.isNaN(major) || major < 18) {
|
|
71
|
+
throw new Error(`Node.js 18+ is required (detected ${process.versions.node}).`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function detectPackageManager() {
|
|
75
|
+
if (commandExists("pnpm")) {
|
|
76
|
+
return "pnpm";
|
|
77
|
+
}
|
|
78
|
+
console.warn("pnpm not detected. Attempting to enable via corepack...");
|
|
79
|
+
const enabled = spawnSync("corepack", ["enable", "pnpm"], { stdio: "inherit" });
|
|
80
|
+
if (enabled.status === 0 && commandExists("pnpm")) {
|
|
81
|
+
return "pnpm";
|
|
82
|
+
}
|
|
83
|
+
console.warn("Falling back to npm. Install pnpm globally for faster installs.");
|
|
84
|
+
return "npm";
|
|
85
|
+
}
|
|
86
|
+
function commandExists(bin) {
|
|
87
|
+
try {
|
|
88
|
+
const result = spawnSync(bin, ["--version"], { stdio: "ignore" });
|
|
89
|
+
return result.status === 0;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function installDependencies(pm) {
|
|
95
|
+
console.log(`Installing dependencies with ${pm}...`);
|
|
96
|
+
const installResult = spawnSync(pm, ["install"], {
|
|
97
|
+
cwd: projectRoot,
|
|
98
|
+
stdio: "inherit"
|
|
99
|
+
});
|
|
100
|
+
if (installResult.status !== 0) {
|
|
101
|
+
throw new Error(`${pm} install failed.`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function ensureEnvFile() {
|
|
105
|
+
const envPath = path.join(projectRoot, ".env");
|
|
106
|
+
const examplePath = path.join(projectRoot, ".env.example");
|
|
107
|
+
if (!existsSync(envPath) && existsSync(examplePath)) {
|
|
108
|
+
copyFileSync(examplePath, envPath);
|
|
109
|
+
console.log("Created .env from .env.example. Remember to fill in your credentials.");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function runServer(pm, transport2) {
|
|
113
|
+
if (!["stdio", "sse"].includes(transport2)) {
|
|
114
|
+
throw new Error(`Unsupported transport "${transport2}". Use stdio or sse.`);
|
|
115
|
+
}
|
|
116
|
+
const script = transport2 === "stdio" ? "dev:stdio" : "dev:sse";
|
|
117
|
+
console.log(`Starting ${transport2} transport via ${pm} run ${script}...`);
|
|
118
|
+
const runner = ["run", script];
|
|
119
|
+
const child = spawnSync(pm, runner, {
|
|
120
|
+
cwd: projectRoot,
|
|
121
|
+
stdio: "inherit",
|
|
122
|
+
env: { ...process.env }
|
|
123
|
+
});
|
|
124
|
+
if (child.status !== 0) {
|
|
125
|
+
throw new Error(`${pm} run ${script} exited with code ${child.status}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=up.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/up.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { spawnSync } from 'node:child_process';\nimport { copyFileSync, existsSync } 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']);\n\ntry {\n ensureNodeVersion();\n const packageManager = detectPackageManager();\n installDependencies(packageManager);\n ensureEnvFile();\n\n if (installOnly) {\n console.log('Dependencies installed. Skipping server launch (--install-only set).');\n process.exit(0);\n }\n\n runServer(packageManager, transport);\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(): 'pnpm' | 'npm' {\n if (commandExists('pnpm')) {\n return 'pnpm';\n }\n\n console.warn('pnpm not detected. Attempting to enable via corepack...');\n const enabled = spawnSync('corepack', ['enable', 'pnpm'], { stdio: 'inherit' });\n if (enabled.status === 0 && commandExists('pnpm')) {\n return 'pnpm';\n }\n\n console.warn('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') {\n console.log(`Installing dependencies with ${pm}...`);\n const installResult = spawnSync(pm, ['install'], {\n cwd: projectRoot,\n stdio: 'inherit',\n });\n if (installResult.status !== 0) {\n throw new Error(`${pm} install failed.`);\n }\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 console.log('Created .env from .env.example. Remember to fill in your credentials.');\n }\n}\n\nfunction runServer(pm: 'pnpm' | 'npm', transport: string) {\n if (!['stdio', 'sse'].includes(transport)) {\n throw new Error(`Unsupported transport \"${transport}\". Use stdio or sse.`);\n }\n const script = transport === 'stdio' ? 'dev:stdio' : 'dev:sse';\n console.log(`Starting ${transport} transport via ${pm} run ${script}...`);\n const runner = ['run', script];\n const child = spawnSync(pm, runner, {\n cwd: projectRoot,\n stdio: 'inherit',\n env: { ...process.env },\n });\n if (child.status !== 0) {\n throw new Error(`${pm} run ${script} exited with code ${child.status}`);\n }\n}\n"],"mappings":";;;;AACA,SAAS,iBAAiB;AAC1B,SAAS,cAAc,kBAAkB;AACzC,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,IAAM,UAAU,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC3D,IAAM,cAAc,KAAK,QAAQ,SAAS,OAAO;AAEjD,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,IAAI,CAAC,WAAW,YAAY,YAAY,YAAY,MAAM;AACxD,YAAU;AACV,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAI,YAAY,MAAM;AACpB,UAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,YAAU;AACV,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,QAAQ,WAAW,KAAK,MAAM,CAAC,CAAC;AACtC,IAAM,aAAa,MAAM,aAAa,SAAS,YAAY;AAC3D,IAAM,cAAc,QAAQ,MAAM,cAAc,CAAC;AAEjD,IAAI;AACF,oBAAkB;AAClB,QAAM,iBAAiB,qBAAqB;AAC5C,sBAAoB,cAAc;AAClC,gBAAc;AAEd,MAAI,aAAa;AACf,YAAQ,IAAI,sEAAsE;AAClF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,YAAU,gBAAgB,SAAS;AACrC,SAAS,OAAO;AACd,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC5D,UAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,YAAY;AACnB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,iDAAwP;AACtQ;AAEA,SAAS,WAAW,QAAkB;AACpC,QAAM,SAAS,CAAC,GAAG,MAAM;AACzB,SAAO,OAAO,OAA4B,CAAC,KAAK,OAAO,UAAU;AAC/D,QAAI,MAAM,WAAW,IAAI,GAAG;AAC1B,YAAM,CAAC,KAAK,WAAW,IAAI,MAAM,MAAM,GAAG;AAC1C,YAAM,aAAa,IAAI,QAAQ,OAAO,EAAE;AACxC,UAAI,gBAAgB,QAAW;AAC7B,YAAI,UAAU,IAAI;AAAA,MACpB,OAAO;AACL,cAAM,OAAO,OAAO,QAAQ,CAAC;AAC7B,YAAI,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAClC,cAAI,UAAU,IAAI;AAClB,iBAAO,OAAO,QAAQ,GAAG,CAAC;AAAA,QAC5B,OAAO;AACL,cAAI,UAAU,IAAI;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;AAEA,SAAS,oBAAoB;AAC3B,QAAM,QAAQ,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AACxD,MAAI,OAAO,MAAM,KAAK,KAAK,QAAQ,IAAI;AACrC,UAAM,IAAI,MAAM,qCAAqC,QAAQ,SAAS,IAAI,IAAI;AAAA,EAChF;AACF;AAEA,SAAS,uBAAuC;AAC9C,MAAI,cAAc,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,UAAQ,KAAK,yDAAyD;AACtE,QAAM,UAAU,UAAU,YAAY,CAAC,UAAU,MAAM,GAAG,EAAE,OAAO,UAAU,CAAC;AAC9E,MAAI,QAAQ,WAAW,KAAK,cAAc,MAAM,GAAG;AACjD,WAAO;AAAA,EACT;AAEA,UAAQ,KAAK,iEAAiE;AAC9E,SAAO;AACT;AAEA,SAAS,cAAc,KAAa;AAClC,MAAI;AACF,UAAM,SAAS,UAAU,KAAK,CAAC,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC;AAChE,WAAO,OAAO,WAAW;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,IAAoB;AAC/C,UAAQ,IAAI,gCAAgC,EAAE,KAAK;AACnD,QAAM,gBAAgB,UAAU,IAAI,CAAC,SAAS,GAAG;AAAA,IAC/C,KAAK;AAAA,IACL,OAAO;AAAA,EACT,CAAC;AACD,MAAI,cAAc,WAAW,GAAG;AAC9B,UAAM,IAAI,MAAM,GAAG,EAAE,kBAAkB;AAAA,EACzC;AACF;AAEA,SAAS,gBAAgB;AACvB,QAAM,UAAU,KAAK,KAAK,aAAa,MAAM;AAC7C,QAAM,cAAc,KAAK,KAAK,aAAa,cAAc;AACzD,MAAI,CAAC,WAAW,OAAO,KAAK,WAAW,WAAW,GAAG;AACnD,iBAAa,aAAa,OAAO;AACjC,YAAQ,IAAI,uEAAuE;AAAA,EACrF;AACF;AAEA,SAAS,UAAU,IAAoBA,YAAmB;AACxD,MAAI,CAAC,CAAC,SAAS,KAAK,EAAE,SAASA,UAAS,GAAG;AACzC,UAAM,IAAI,MAAM,0BAA0BA,UAAS,sBAAsB;AAAA,EAC3E;AACA,QAAM,SAASA,eAAc,UAAU,cAAc;AACrD,UAAQ,IAAI,YAAYA,UAAS,kBAAkB,EAAE,QAAQ,MAAM,KAAK;AACxE,QAAM,SAAS,CAAC,OAAO,MAAM;AAC7B,QAAM,QAAQ,UAAU,IAAI,QAAQ;AAAA,IAClC,KAAK;AAAA,IACL,OAAO;AAAA,IACP,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,EACxB,CAAC;AACD,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,MAAM,GAAG,EAAE,QAAQ,MAAM,qBAAqB,MAAM,MAAM,EAAE;AAAA,EACxE;AACF;","names":["transport"]}
|
package/dist/index.d.ts
ADDED