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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Full command reference for the hscli command-line interface.

<!-- AUTO-GENERATED from the oclif manifest by scripts/gen-commands.mjs — do not edit by hand. -->

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

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@wavyx/hscli",
"version": "0.10.1",
"version": "0.10.2",
"publishConfig": {
"access": "public"
},
Expand Down
4 changes: 3 additions & 1 deletion src/lib/mcp/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Expand Down
2 changes: 1 addition & 1 deletion src/lib/mcp/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down
25 changes: 16 additions & 9 deletions test/lib/mcp/catalog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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({})
})
})
6 changes: 4 additions & 2 deletions test/lib/mcp/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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,
})
})
})
Expand Down
2 changes: 1 addition & 1 deletion website/src/content/docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ hscli <group> <action> [target] [flags]
```

Run `hscli <group> --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

Expand Down
15 changes: 15 additions & 0 deletions website/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,21 @@ const description =
<div class="row">&nbsp;&nbsp;<span class="t-flag">--output</span> csv &gt; may.csv</div>
</div>
</article>
<article class="recipe recipe--wide">
<div class="recipe__head">
<span class="recipe__tag"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="6" width="14" height="12" rx="2"></rect><path d="M9 2v4M15 2v4M9 18v4M15 18v4M2 9h3M2 14h3M19 9h3M19 14h3"></path></svg>MCP</span>
<h3>Plug hscli into any agent</h3>
<p>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; <code>--allow-writes</code> lets the agent act.</p>
<a class="recipe__link" href={`${base}automation/mcp/`}>MCP server guide →</a>
</div>
<div class="recipe__code">
<div class="row"><span class="t-comment"># one line — Claude Code now has hscli as native tools</span></div>
<div class="row"><span class="t-prompt">$</span> <span class="t-cmd">claude</span> <span class="t-sub">mcp add</span> hscli <span class="t-punct">--</span> <span class="t-cmd">hscli</span> <span class="t-sub">mcp serve</span></div>
<div class="row out"><span class="t-str">✓</span> hscli connected <span class="t-dim">· 47 read-only tools</span></div>
<div class="row"><span class="t-prompt">$</span> <span class="t-cmd">claude</span> <span class="t-flag">-p</span> <span class="t-str">"triage today's billing tickets"</span></div>
<div class="row out"><span class="t-dim">→</span> conv_list <span class="t-dim">·</span> conv_get <span class="t-dim">·</span> structured JSON <span class="t-dim">· exit 0</span></div>
</div>
</article>
<article class="recipe recipe--wide">
<div class="recipe__head">
<span class="recipe__tag"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>Docs</span>
Expand Down
1 change: 1 addition & 0 deletions website/src/styles/home.css
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
Loading