diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a354d4..4815778 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.10.2] - 2026-06-05 + +### Added + +- Home page: a featured **MCP** section showcasing `hscli mcp serve` for AI agents, alongside the Docs block. + +### Changed + +- MCP: no longer expose `doctor` (a local-environment diagnostic that makes a live network probe) or `mcp serve` itself (calling it would spawn a nested server) as tools; read tools now carry `idempotentHint` so clients can cache/retry them safely. + ## [0.10.1] - 2026-06-05 ### Fixed diff --git a/docs/commands.md b/docs/commands.md index d23d393..743d73d 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -5,7 +5,7 @@ description: Full command reference for the hscli command-line interface. -Reference for `hscli` v0.10.1 (89 commands). Every command also accepts the global flags `--output table|json|yaml|csv`, `--jq`, `--fields`, `--profile`, `--no-color`, `--verbose`, `--no-retry`, and `--timeout`. +Reference for `hscli` v0.10.2 (89 commands). Every command also accepts the global flags `--output table|json|yaml|csv`, `--jq`, `--fields`, `--profile`, `--no-color`, `--verbose`, `--no-retry`, and `--timeout`. ## Top-level diff --git a/package-lock.json b/package-lock.json index 360856c..37cbfd5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@wavyx/hscli", - "version": "0.10.1", + "version": "0.10.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@wavyx/hscli", - "version": "0.10.1", + "version": "0.10.2", "license": "MIT", "dependencies": { "@inquirer/prompts": "8.5.2", diff --git a/package.json b/package.json index bd9c7a9..b43a2c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wavyx/hscli", - "version": "0.10.1", + "version": "0.10.2", "publishConfig": { "access": "public" }, diff --git a/src/lib/mcp/catalog.js b/src/lib/mcp/catalog.js index d811417..35dacb0 100644 --- a/src/lib/mcp/catalog.js +++ b/src/lib/mcp/catalog.js @@ -6,7 +6,9 @@ // Commands never exposed as MCP tools: // - `api` is an arbitrary-request escape hatch that bypasses per-tool gating. // - `conv:watch` is a long-running stream that doesn't fit request/response. -export const EXCLUDED = new Set(['api', 'conv:watch']) +// - `doctor` is a local-environment diagnostic (live network probe), not useful to an agent. +// - `mcp:serve` is this server itself — exposing it would let a tool spawn another server. +export const EXCLUDED = new Set(['api', 'conv:watch', 'doctor', 'mcp:serve']) // Topics whose every command is read-only. const READ_TOPICS = new Set(['report', 'beacon']) diff --git a/src/lib/mcp/server.js b/src/lib/mcp/server.js index 50695b9..4ba1126 100644 --- a/src/lib/mcp/server.js +++ b/src/lib/mcp/server.js @@ -9,7 +9,7 @@ export function annotationsFor(entry) { title: entry.summary, readOnlyHint: entry.kind === 'read', destructiveHint: entry.kind === 'destructive', - idempotentHint: false, + idempotentHint: entry.kind === 'read', openWorldHint: true, } } diff --git a/test/lib/mcp/catalog.test.js b/test/lib/mcp/catalog.test.js index 4af66d4..90fe5c1 100644 --- a/test/lib/mcp/catalog.test.js +++ b/test/lib/mcp/catalog.test.js @@ -42,15 +42,22 @@ describe('buildCatalog', () => { { id: 'conv:delete', description: 'Delete', flags: {}, args: {} }, { id: 'api', summary: 'escape hatch', flags: {}, args: {} }, { id: 'conv:watch', summary: 'watch', flags: {}, args: {} }, + { id: 'doctor', summary: 'diagnostics', flags: {}, args: {} }, + { id: 'mcp:serve', summary: 'serve', flags: {}, args: {} }, { id: 'secret', summary: 'hidden one', hidden: true, flags: {}, args: {} }, - { id: 'doctor' }, // bare: no summary/description/flags/args + { id: 'version' }, // bare: no summary/description/flags/args ] - it('excludes hidden + escape-hatch + streaming commands, sorts by id', () => { + it('excludes hidden, escape-hatch, streaming, diagnostic, and self commands', () => { const cat = buildCatalog(commands) - expect(cat.map((t) => t.id)).toEqual(['conv:delete', 'conv:list', 'doctor']) - expect(EXCLUDED.has('api')).toBe(true) - expect(EXCLUDED.has('conv:watch')).toBe(true) + expect(cat.map((t) => t.id)).toEqual([ + 'conv:delete', + 'conv:list', + 'version', + ]) + for (const id of ['api', 'conv:watch', 'doctor', 'mcp:serve']) { + expect(EXCLUDED.has(id)).toBe(true) + } }) it('maps id, toolName, summary and kind', () => { @@ -67,9 +74,9 @@ describe('buildCatalog', () => { }) it('falls back to the id for summary and defaults flags/args when absent', () => { - const doctor = buildCatalog(commands).find((t) => t.id === 'doctor') - expect(doctor.summary).toBe('doctor') - expect(doctor.flags).toEqual({}) - expect(doctor.args).toEqual({}) + const v = buildCatalog(commands).find((t) => t.id === 'version') + expect(v.summary).toBe('version') + expect(v.flags).toEqual({}) + expect(v.args).toEqual({}) }) }) diff --git a/test/lib/mcp/server.test.js b/test/lib/mcp/server.test.js index 5e4d3bf..9305972 100644 --- a/test/lib/mcp/server.test.js +++ b/test/lib/mcp/server.test.js @@ -45,10 +45,11 @@ describe('selectTools', () => { }) describe('annotationsFor', () => { - it('flags reads read-only', () => { + it('flags reads read-only and idempotent', () => { expect(annotationsFor({ kind: 'read', summary: 'x' })).toMatchObject({ readOnlyHint: true, destructiveHint: false, + idempotentHint: true, }) }) it('flags destructive tools destructive', () => { @@ -59,10 +60,11 @@ describe('annotationsFor', () => { }, ) }) - it('flags writes as neither read-only nor destructive', () => { + it('flags writes as neither read-only nor destructive nor idempotent', () => { expect(annotationsFor({ kind: 'write', summary: 'x' })).toMatchObject({ readOnlyHint: false, destructiveHint: false, + idempotentHint: false, }) }) }) diff --git a/website/src/content/docs/reference/commands.mdx b/website/src/content/docs/reference/commands.mdx index db1a79f..167f827 100644 --- a/website/src/content/docs/reference/commands.mdx +++ b/website/src/content/docs/reference/commands.mdx @@ -12,7 +12,7 @@ hscli [target] [flags] ``` Run `hscli --help` for the live, self-describing version of any command. -This page lists all 89 commands in `hscli` v0.10.1. +This page lists all 89 commands in `hscli` v0.10.2. ## alias diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 53fba70..872b884 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -210,6 +210,21 @@ const description =
  --output csv > may.csv
+
+
+ MCP +

Plug hscli into any agent

+

Run hscli as a Model Context Protocol server and Claude — Desktop, Code, or any MCP client — gets one typed tool per command. Reads by default; --allow-writes lets the agent act.

+ MCP server guide → +
+
+
# one line — Claude Code now has hscli as native tools
+
$ claude mcp add hscli -- hscli mcp serve
+
hscli connected · 47 read-only tools
+
$ claude -p "triage today's billing tickets"
+
conv_list · conv_get · structured JSON · exit 0
+
+
Docs diff --git a/website/src/styles/home.css b/website/src/styles/home.css index 9dd8dcd..1eeb49b 100644 --- a/website/src/styles/home.css +++ b/website/src/styles/home.css @@ -141,6 +141,7 @@ .recipe__tag svg { width: 14px; height: 14px; } .recipe h3 { font-family: var(--font-display); font-weight: 600; font-size: 1.08rem; margin: 0 0 6px; color: var(--fg); letter-spacing: -.01em; } .recipe p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--fg-muted); } +.recipe__head code { font-family: var(--font-mono); font-size: 0.86em; color: var(--accent); font-variant-ligatures: none; } .recipe__code { margin-top: auto; background: var(--code-bg); border-top: 1px solid var(--code-border); padding: 14px 16px; font-family: var(--font-mono); font-size: 12px; line-height: 1.7; overflow-x: auto; color: var(--code-fg); } .recipe__code .row { white-space: pre; }