@hirey/hi-mcp-server 0.1.12 → 0.1.14

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/README.md CHANGED
@@ -86,6 +86,8 @@ npm install -g @hirey/hi-mcp-server
86
86
  }
87
87
  ```
88
88
 
89
+ 这里的 `HI_MCP_STATE_DIR` 必须直接指向 profile 对应的叶子目录;不要只写父目录 `/Users/you/.openclaw/hi-mcp`,否则虽然 `openclaw-main.json` 还能落盘,但 receiver config 和 doctor 会把它识别成非 canonical OpenClaw 布局。
90
+
89
91
  普通用户首次接入后的典型顺序:
90
92
 
91
93
  1. 用宿主把 `hi-mcp-server` 作为 `stdio` MCP server 挂上
@@ -100,7 +102,7 @@ npm install -g @hirey/hi-mcp-server
100
102
 
101
103
  对 OpenClaw 的普通用户本地 vendor 安装,不要显式传 `receiver_command="hi-agent-receiver"` 或 `receiver_command_argv=["hi-agent-receiver"]`。这两种旧形态都会重新把启动语义写回“按 PATH 找全局命令”。OpenClaw 要么不传 receiver 启动覆盖,让安装流默认落到 `~/.openclaw/vendor/hi/node_modules/.bin/hi-agent-receiver`,要么只传指向该本地 vendor binary 的精确 `receiver_command_argv`。
102
104
 
103
- 对 OpenClaw 的普通用户安装,默认就应该把当前聊天绑成 Hi 以后回来的默认线程。因此 `hi_agent_install` 应直接带上 `host_session_key`、`default_reply_channel`、`default_reply_to`、`default_reply_account_id`、`default_reply_thread_id`、`route_missing_policy`。这组字段会同时写进 installation 的 `delivery_capabilities` 和本地 receiver config,避免出现 gateway 已经知道默认 route、但本地 receiver 还沿旧配置投递的漂移。如果拿不到当前聊天的 canonical `host_session_key`,OpenClaw 安装不应宣告成功,而应先把 continuity blocker 说明白。
105
+ 对 OpenClaw 的普通用户安装,默认就应该把当前聊天绑成 Hi 以后回来的默认线程。因此 `hi_agent_install` 应直接带上 `host_session_key`、`default_reply_channel`、`default_reply_to`、`default_reply_account_id`、`default_reply_thread_id`、`route_missing_policy`。这组字段会同时写进 installation 的 `delivery_capabilities` 和本地 receiver config,避免出现 gateway 已经知道默认 route、但本地 receiver 还沿旧配置投递的漂移。如果拿不到当前聊天的 canonical `host_session_key`,OpenClaw 安装不应宣告成功,而应先把 continuity blocker 说明白。对于 OpenClaw,如果宿主只暴露了当前聊天的展示层 channel(例如 `webchat`)而没有更具体的 `to/account_id/thread_id`,`hi_agent_install` 必须把默认 continuation channel 规范成 `last`;不要把这类 display-only channel 原样写进 `default_reply_route`,否则 local receiver 的 delivery probe 会直接失败。
104
106
 
105
107
  安装后如果要显式验证 continuation route,可以用 `hi_agent_test_delivery`:既可以直接传 `host_session_key` / `reply_*` 做一次显式 route probe,也可以在 installation 已设置 `default_reply_route` 时不传这些字段,让 gateway 自动沿当前 installation 的默认 route 发 probe。
106
108
 
@@ -124,11 +126,18 @@ npm install -g @hirey/hi-mcp-server
124
126
  - `HI_MCP_STATE_DIR`
125
127
  - 本地 state 目录。未显式指定时,默认落到稳定 home 目录:`~/.hirey/hi-mcp/<profile>`。
126
128
  - OpenClaw 普通用户建议显式固定到 canonical 目录,例如 `/Users/you/.openclaw/hi-mcp/openclaw-main`,方便后续 `hi-agent-receiver` 直接复用同一份身份 state。
129
+ - 不要把它设置成 bare parent dir `/Users/you/.openclaw/hi-mcp`;OpenClaw 场景下必须把 profile 叶子目录也包含进去。
127
130
 
128
131
  ### 本地 state
129
132
 
130
133
  state 会按 profile 落到 `HI_MCP_STATE_DIR/<profile>.json`。
131
134
 
135
+ 对 OpenClaw 而言,推荐的 canonical 形态就是:
136
+
137
+ - `HI_MCP_PROFILE=openclaw-main`
138
+ - `HI_MCP_STATE_DIR=/Users/you/.openclaw/hi-mcp/openclaw-main`
139
+ - 最终 state file = `/Users/you/.openclaw/hi-mcp/openclaw-main/openclaw-main.json`
140
+
132
141
  文件内部明确分成两块:
133
142
 
134
143
  - `identity`
@@ -1 +1 @@
1
- {"version":3,"file":"defaultReplyRoute.d.ts","sourceRoot":"","sources":["../src/defaultReplyRoute.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,wBAAgB,yCAAyC,CAAC,IAAI,EAAE;IAC9D,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GAAG,2BAA2B,GAAG,IAAI,CAyBrC;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACpD,GAAG;IACF,EAAE,EAAE,IAAI,CAAC;IACT,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG;IACF,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,sCAAsC,CAAC;CAC/C,CAcA"}
1
+ {"version":3,"file":"defaultReplyRoute.d.ts","sourceRoot":"","sources":["../src/defaultReplyRoute.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,wBAAgB,yCAAyC,CAAC,IAAI,EAAE;IAC9D,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GAAG,2BAA2B,GAAG,IAAI,CAkDrC;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACrD,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACpD,GAAG;IACF,EAAE,EAAE,IAAI,CAAC;IACT,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG;IACF,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,sCAAsC,CAAC;CAC/C,CAcA"}
@@ -12,8 +12,28 @@ export function resolveInstallDefaultReplyDeliveryContext(args) {
12
12
  || explicit.to
13
13
  || explicit.account_id
14
14
  || explicit.thread_id);
15
- if (hasExplicit)
15
+ if (hasExplicit) {
16
+ const hasConcreteReplyTarget = !!(explicit.to
17
+ || explicit.account_id
18
+ || explicit.thread_id);
19
+ const openClawSupportedDisplayChannel = explicit.channel === 'last' || explicit.channel === 'imessage';
20
+ // OpenClaw may surface the current chat as a display-only channel like `webchat`
21
+ // without any concrete target fields. In that case the durable continuation lane
22
+ // is still `last`, not the display channel value.
23
+ if (args.hostKind === 'openclaw'
24
+ && args.hasSessionKey
25
+ && explicit.channel
26
+ && !hasConcreteReplyTarget
27
+ && !openClawSupportedDisplayChannel) {
28
+ return {
29
+ channel: 'last',
30
+ to: null,
31
+ account_id: null,
32
+ thread_id: null,
33
+ };
34
+ }
16
35
  return explicit;
36
+ }
17
37
  // OpenClaw hooks continuation accepts `last|imessage`; when we already have the
18
38
  // canonical session key, `last` is the canonical "same chat" lane.
19
39
  if (args.hostKind === 'openclaw' && args.hasSessionKey) {
package/dist/server.js CHANGED
@@ -11,7 +11,7 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
11
11
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
12
12
  import { AGENT_GATEWAY_EVENT_TOPICS, normalizeAgentEndpointList, normalizeAgentInstallationDeliveryDeclaration, normalizeAgentSubscriptionList, normalizeText, } from '@hirey/hi-agent-contracts';
13
13
  import { createHiAgentClients, exchangeHiAgentClientCredentialsToken, HiAgentGatewayClient, HiAgentPlatformClient, } from '@hirey/hi-agent-sdk';
14
- import { readState, resolveCanonicalOpenClawStateDir, resolveDefaultStateDir, resolveLegacyStateFiles, resolveStateFile, updateState, normalizeStateProfile, } from './state.js';
14
+ import { readState, resolveCanonicalOpenClawStateDir, resolveDefaultStateDir, resolveLegacyStateFiles, resolveStateFile, validateOpenClawStateDir, updateState, normalizeStateProfile, } from './state.js';
15
15
  import { looksLikeOpenClawSessionKey, validateOpenClawSessionKey, } from './openclaw-session-key.js';
16
16
  import { buildInstallReceiverCommandArgv, } from './receiver-command.js';
17
17
  import { applyReceiverRuntimeSnapshot, receiverConfigMaterialEquals, } from './receiver-config-material.js';
@@ -836,6 +836,7 @@ function buildDoctorSummary(args) {
836
836
  route_missing_policy: routeMissingPolicy,
837
837
  default_reply_route: defaultReplyRoute,
838
838
  default_reply_route_validation: args.defaultReplyRouteValidation,
839
+ openclaw_state_dir_validation: args.openClawStateDirValidation,
839
840
  delivery_probe: args.deliveryProbe,
840
841
  delivery_probe_diagnostics: args.deliveryProbeDiagnostics,
841
842
  };
@@ -1087,11 +1088,23 @@ async function handleDoctor(args) {
1087
1088
  reason: null,
1088
1089
  session_key: null,
1089
1090
  };
1091
+ let openClawStateDirValidation = null;
1090
1092
  if (!state.identity) {
1091
1093
  blockers.push('missing_agent_identity');
1092
1094
  }
1093
- if (installState.host_kind === 'openclaw' && config.stateDir !== resolveCanonicalOpenClawStateDir(config.profile)) {
1094
- warnings.push('openclaw_state_dir_not_canonical');
1095
+ if (installState.host_kind === 'openclaw') {
1096
+ const validation = validateOpenClawStateDir(config.stateDir, config.profile);
1097
+ openClawStateDirValidation = {
1098
+ configured_state_dir: validation.configured_state_dir,
1099
+ canonical_state_dir: validation.canonical_state_dir,
1100
+ reason: validation.reason,
1101
+ };
1102
+ if (!validation.is_canonical) {
1103
+ warnings.push('openclaw_state_dir_not_canonical');
1104
+ if (validation.reason === 'missing_profile_leaf') {
1105
+ warnings.push('openclaw_state_dir_missing_profile_leaf');
1106
+ }
1107
+ }
1095
1108
  }
1096
1109
  if (includeRemote && state.identity) {
1097
1110
  try {
@@ -1198,6 +1211,7 @@ async function handleDoctor(args) {
1198
1211
  deliveryProbe,
1199
1212
  deliveryProbeDiagnostics,
1200
1213
  defaultReplyRouteValidation,
1214
+ openClawStateDirValidation,
1201
1215
  }));
1202
1216
  }
1203
1217
  async function handleInstall(args) {
package/dist/state.d.ts CHANGED
@@ -40,6 +40,13 @@ export declare const DEFAULT_HI_MCP_PROFILE = "default";
40
40
  export declare function normalizeStateProfile(raw: unknown): string;
41
41
  export declare function resolveDefaultStateDir(profileRaw: unknown): string;
42
42
  export declare function resolveCanonicalOpenClawStateDir(profileRaw?: unknown): string;
43
+ export type OpenClawStateDirValidation = {
44
+ configured_state_dir: string;
45
+ canonical_state_dir: string;
46
+ is_canonical: boolean;
47
+ reason: 'canonical' | 'missing_profile_leaf' | 'non_canonical';
48
+ };
49
+ export declare function validateOpenClawStateDir(stateDirRaw: unknown, profileRaw?: unknown): OpenClawStateDirValidation;
43
50
  export declare function resolveLegacyStateFiles(profileRaw: unknown): string[];
44
51
  export declare function buildDefaultState(profile: string): HiAgentPersistedState;
45
52
  export declare function resolveStateFile(args: {
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;QACxC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;IACF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC;CAC9B,CAAC;AAeF,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAIlE;AAED,wBAAgB,gCAAgC,CAAC,UAAU,GAAE,OAAyB,GAAG,MAAM,CAG9F;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,EAAE,CAMrE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAOxE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,UAG3E;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAoCjC;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,qBAAqB,CAAC;CAC9B,iBAIA;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,qBAAqB,CAAC;CACpE,kCASA"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
package/dist/state.js CHANGED
@@ -17,6 +17,10 @@ export const DEFAULT_HI_MCP_PROFILE = 'default';
17
17
  export function normalizeStateProfile(raw) {
18
18
  return String(raw || '').trim() || DEFAULT_HI_MCP_PROFILE;
19
19
  }
20
+ function normalizeStateDirPath(raw) {
21
+ const text = typeof raw === 'string' ? String(raw).trim() : '';
22
+ return text ? path.resolve(text) : '';
23
+ }
20
24
  export function resolveDefaultStateDir(profileRaw) {
21
25
  const profile = normalizeStateProfile(profileRaw);
22
26
  // 本地 installation state 必须落到稳定 home 目录,不能继续跟着 cwd 漂。
@@ -26,6 +30,34 @@ export function resolveCanonicalOpenClawStateDir(profileRaw = 'openclaw-main') {
26
30
  const profile = normalizeStateProfile(profileRaw);
27
31
  return path.join(os.homedir(), '.openclaw', 'hi-mcp', profile);
28
32
  }
33
+ export function validateOpenClawStateDir(stateDirRaw, profileRaw = 'openclaw-main') {
34
+ const canonicalStateDir = resolveCanonicalOpenClawStateDir(profileRaw);
35
+ const configuredStateDir = normalizeStateDirPath(stateDirRaw) || canonicalStateDir;
36
+ if (configuredStateDir === canonicalStateDir) {
37
+ return {
38
+ configured_state_dir: configuredStateDir,
39
+ canonical_state_dir: canonicalStateDir,
40
+ is_canonical: true,
41
+ reason: 'canonical',
42
+ };
43
+ }
44
+ const bareOpenClawStateDir = path.join(os.homedir(), '.openclaw', 'hi-mcp');
45
+ if (configuredStateDir === bareOpenClawStateDir) {
46
+ // OpenClaw 的 canonical 布局要求 stateDir 自己就是 profile 叶子目录,不能只指向父目录。
47
+ return {
48
+ configured_state_dir: configuredStateDir,
49
+ canonical_state_dir: canonicalStateDir,
50
+ is_canonical: false,
51
+ reason: 'missing_profile_leaf',
52
+ };
53
+ }
54
+ return {
55
+ configured_state_dir: configuredStateDir,
56
+ canonical_state_dir: canonicalStateDir,
57
+ is_canonical: false,
58
+ reason: 'non_canonical',
59
+ };
60
+ }
29
61
  export function resolveLegacyStateFiles(profileRaw) {
30
62
  const profile = normalizeStateProfile(profileRaw);
31
63
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hirey/hi-mcp-server",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/server.js",