hivecode-mcp 0.12.2 → 0.12.4
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/LICENSE +21 -21
- package/README.md +187 -8
- package/core.js +91 -91
- package/docs/HIVE_RULES.md +41 -41
- package/docs/MCP.md +5 -5
- package/docs/SELF_HOSTING.md +256 -256
- package/hive-activity.js +336 -310
- package/hive-await.js +187 -0
- package/hive-branch.js +656 -0
- package/hive-busy.js +88 -88
- package/hive-coord.js +110 -110
- package/hive-gate.js +135 -135
- package/hive-hook-install.js +179 -103
- package/hive-instructions.js +74 -66
- package/hive-listen.js +81 -0
- package/hive-mailbox.js +517 -386
- package/hive-mcp.js +483 -126
- package/hive-mux-proto.js +58 -58
- package/hive-mux.js +350 -350
- package/hive-obligations.js +247 -247
- package/hive-pr-staleness.js +189 -189
- package/hive-pr.js +337 -0
- package/hive-privacy.js +327 -288
- package/hive-receipts.js +233 -233
- package/hive-relay-health.js +7 -2
- package/hive-secrets.js +89 -0
- package/hive-setup.js +548 -445
- package/hive-stdio.js +172 -168
- package/hive-stop-hook.js +290 -241
- package/hive-throttle.js +118 -118
- package/hive-update.js +58 -58
- package/hive-version.js +110 -59
- package/icr-merge.js +58 -58
- package/icr.js +121 -15
- package/lang-json.js +211 -211
- package/lang-ruby.js +230 -230
- package/lang-toml.js +136 -136
- package/lang-yaml.js +183 -183
- package/limits.js +413 -394
- package/package.json +19 -11
- package/reconcile.cjs +614 -477
- package/substrate.js +252 -252
- package/sync.js +4691 -3011
- package/grm-link.cjs +0 -141
package/hive-stdio.js
CHANGED
|
@@ -1,168 +1,172 @@
|
|
|
1
|
-
// hive-stdio.js -- the MCP stdio server Hivecode actually needs, without the SDK's web stack.
|
|
2
|
-
//
|
|
3
|
-
// WHY THIS EXISTS. @modelcontextprotocol/sdk is a client AND a server AND three HTTP transports,
|
|
4
|
-
// so installing it installs express, hono, cors, jose, zod, ajv and ~85 other packages -- 25 MB,
|
|
5
|
-
// most of a stranger's first `npx hivecode-mcp` download (36.7s measured cold, 25 Sep 2026). A
|
|
6
|
-
// load trace of a full session (join, claim, write, release, say, wait) showed the client uses
|
|
7
|
-
// exactly one slice of it: newline-delimited JSON-RPC over stdin/stdout, `initialize`, `ping`,
|
|
8
|
-
// `tools/list`, `tools/call`, and two outgoing notifications. That slice is this file.
|
|
9
|
-
//
|
|
10
|
-
// IT MUST BEHAVE EXACTLY LIKE THE SDK WHERE A CLIENT CAN TELL. The SDK is still a devDependency,
|
|
11
|
-
// and tests/mcp-conformance-test.mjs drives this server with the SDK's own CLIENT -- the one
|
|
12
|
-
// Claude Code and the Gemini CLI are built on. Specifically, as in the SDK (1.30):
|
|
13
|
-
// - protocol version: echo the client's if supported, else answer with the newest we know;
|
|
14
|
-
// - `ping` -> {}; an unknown method -> error -32601; a handler that throws -> its own numeric
|
|
15
|
-
// `code` if it has one, else -32603, with the error's message;
|
|
16
|
-
// - a request the client CANCELLED gets no response (the spec: "SHOULD NOT send a response");
|
|
17
|
-
// - a line that is not JSON is logged to stderr and otherwise ignored, never answered.
|
|
18
|
-
// And one thing the SDK does not do: when stdin closes, the server says so (onclose), so the
|
|
19
|
-
// process can EXIT. The spec asks exactly that of a stdio server; the SDK left it running, and a
|
|
20
|
-
// Hivecode server that outlives its agent app keeps a seat in the room for a member who is gone.
|
|
21
|
-
|
|
22
|
-
export const LATEST_PROTOCOL_VERSION = '2025-11-25'
|
|
23
|
-
export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, '2025-06-18', '2025-03-26', '2024-11-05', '2024-10-07']
|
|
24
|
-
|
|
25
|
-
// Same shape hive-mcp.js used to import from '@modelcontextprotocol/sdk/types.js': something
|
|
26
|
-
// setRequestHandler can take the method name from.
|
|
27
|
-
export const ListToolsRequestSchema = Object.freeze({ method: 'tools/list' })
|
|
28
|
-
export const CallToolRequestSchema = Object.freeze({ method: 'tools/call' })
|
|
29
|
-
|
|
30
|
-
const METHOD_NOT_FOUND = -32601
|
|
31
|
-
const INVALID_PARAMS = -32602
|
|
32
|
-
const INTERNAL_ERROR = -32603
|
|
33
|
-
|
|
34
|
-
export class Server {
|
|
35
|
-
constructor(serverInfo, { capabilities = {}, instructions } = {}) {
|
|
36
|
-
this._serverInfo = serverInfo
|
|
37
|
-
this._capabilities = capabilities
|
|
38
|
-
this._instructions = instructions
|
|
39
|
-
// Keyed by method, called as handler(request, extra) -- the SDK's own internal shape, which
|
|
40
|
-
// the test seam in hive-mcp.js reaches into on purpose.
|
|
41
|
-
this._requestHandlers = new Map()
|
|
42
|
-
this._transport = null
|
|
43
|
-
this._inflight = new Map() // request id -> AbortController, so a cancel can find it
|
|
44
|
-
this.onclose = null
|
|
45
|
-
this.oninitialized = null
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
this.
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
// hive-stdio.js -- the MCP stdio server Hivecode actually needs, without the SDK's web stack.
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS EXISTS. @modelcontextprotocol/sdk is a client AND a server AND three HTTP transports,
|
|
4
|
+
// so installing it installs express, hono, cors, jose, zod, ajv and ~85 other packages -- 25 MB,
|
|
5
|
+
// most of a stranger's first `npx hivecode-mcp` download (36.7s measured cold, 25 Sep 2026). A
|
|
6
|
+
// load trace of a full session (join, claim, write, release, say, wait) showed the client uses
|
|
7
|
+
// exactly one slice of it: newline-delimited JSON-RPC over stdin/stdout, `initialize`, `ping`,
|
|
8
|
+
// `tools/list`, `tools/call`, and two outgoing notifications. That slice is this file.
|
|
9
|
+
//
|
|
10
|
+
// IT MUST BEHAVE EXACTLY LIKE THE SDK WHERE A CLIENT CAN TELL. The SDK is still a devDependency,
|
|
11
|
+
// and tests/mcp-conformance-test.mjs drives this server with the SDK's own CLIENT -- the one
|
|
12
|
+
// Claude Code and the Gemini CLI are built on. Specifically, as in the SDK (1.30):
|
|
13
|
+
// - protocol version: echo the client's if supported, else answer with the newest we know;
|
|
14
|
+
// - `ping` -> {}; an unknown method -> error -32601; a handler that throws -> its own numeric
|
|
15
|
+
// `code` if it has one, else -32603, with the error's message;
|
|
16
|
+
// - a request the client CANCELLED gets no response (the spec: "SHOULD NOT send a response");
|
|
17
|
+
// - a line that is not JSON is logged to stderr and otherwise ignored, never answered.
|
|
18
|
+
// And one thing the SDK does not do: when stdin closes, the server says so (onclose), so the
|
|
19
|
+
// process can EXIT. The spec asks exactly that of a stdio server; the SDK left it running, and a
|
|
20
|
+
// Hivecode server that outlives its agent app keeps a seat in the room for a member who is gone.
|
|
21
|
+
|
|
22
|
+
export const LATEST_PROTOCOL_VERSION = '2025-11-25'
|
|
23
|
+
export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, '2025-06-18', '2025-03-26', '2024-11-05', '2024-10-07']
|
|
24
|
+
|
|
25
|
+
// Same shape hive-mcp.js used to import from '@modelcontextprotocol/sdk/types.js': something
|
|
26
|
+
// setRequestHandler can take the method name from.
|
|
27
|
+
export const ListToolsRequestSchema = Object.freeze({ method: 'tools/list' })
|
|
28
|
+
export const CallToolRequestSchema = Object.freeze({ method: 'tools/call' })
|
|
29
|
+
|
|
30
|
+
const METHOD_NOT_FOUND = -32601
|
|
31
|
+
const INVALID_PARAMS = -32602
|
|
32
|
+
const INTERNAL_ERROR = -32603
|
|
33
|
+
|
|
34
|
+
export class Server {
|
|
35
|
+
constructor(serverInfo, { capabilities = {}, instructions } = {}) {
|
|
36
|
+
this._serverInfo = serverInfo
|
|
37
|
+
this._capabilities = capabilities
|
|
38
|
+
this._instructions = instructions
|
|
39
|
+
// Keyed by method, called as handler(request, extra) -- the SDK's own internal shape, which
|
|
40
|
+
// the test seam in hive-mcp.js reaches into on purpose.
|
|
41
|
+
this._requestHandlers = new Map()
|
|
42
|
+
this._transport = null
|
|
43
|
+
this._inflight = new Map() // request id -> AbortController, so a cancel can find it
|
|
44
|
+
this.onclose = null
|
|
45
|
+
this.oninitialized = null
|
|
46
|
+
this._clientInfo = null // who connected: the app's name and version, from initialize
|
|
47
|
+
this._requestHandlers.set('initialize', (req) => {
|
|
48
|
+
const requested = req.params && req.params.protocolVersion
|
|
49
|
+
this._clientInfo = (req.params && req.params.clientInfo) || null
|
|
50
|
+
return {
|
|
51
|
+
protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(requested) ? requested : LATEST_PROTOCOL_VERSION,
|
|
52
|
+
capabilities: this._capabilities,
|
|
53
|
+
serverInfo: this._serverInfo,
|
|
54
|
+
...(this._instructions ? { instructions: this._instructions } : {}),
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
this._requestHandlers.set('ping', () => ({}))
|
|
58
|
+
}
|
|
59
|
+
/** The connecting app's clientInfo ({ name, version }), as the SDK's Server exposes it. */
|
|
60
|
+
getClientVersion() { return this._clientInfo || undefined }
|
|
61
|
+
|
|
62
|
+
setRequestHandler(schema, handler) {
|
|
63
|
+
const method = typeof schema === 'string' ? schema : schema && schema.method
|
|
64
|
+
if (!method) throw new Error('setRequestHandler needs a method')
|
|
65
|
+
this._requestHandlers.set(method, handler)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async connect(transport) {
|
|
69
|
+
this._transport = transport
|
|
70
|
+
transport.onmessage = (msg) => this._onmessage(msg)
|
|
71
|
+
transport.onclose = () => { this._transport = null; if (this.onclose) this.onclose() }
|
|
72
|
+
await transport.start()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Send a notification to the client. Rejects when there is nobody to send it to. */
|
|
76
|
+
notification({ method, params }) {
|
|
77
|
+
if (!this._transport) return Promise.reject(new Error('Not connected'))
|
|
78
|
+
return this._transport.send({ jsonrpc: '2.0', method, ...(params !== undefined ? { params } : {}) })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_onmessage(msg) {
|
|
82
|
+
if (Array.isArray(msg)) {
|
|
83
|
+
// JSON-RPC batches: the 2025-03-26 revision allowed them. Answer each request in the batch;
|
|
84
|
+
// the replies go back one per line, which every client matches by id.
|
|
85
|
+
for (const m of msg) this._onmessage(m)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
if (!msg || typeof msg !== 'object' || msg.jsonrpc !== '2.0') return
|
|
89
|
+
if (msg.method === undefined) return // a response to a request we never send
|
|
90
|
+
if (msg.id === undefined || msg.id === null) return this._onnotification(msg)
|
|
91
|
+
this._onrequest(msg)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_onnotification(msg) {
|
|
95
|
+
if (msg.method === 'notifications/initialized') { if (this.oninitialized) this.oninitialized(); return }
|
|
96
|
+
if (msg.method === 'notifications/cancelled') {
|
|
97
|
+
const id = msg.params && msg.params.requestId
|
|
98
|
+
const ac = this._inflight.get(id)
|
|
99
|
+
if (ac) ac.abort()
|
|
100
|
+
}
|
|
101
|
+
// every other notification is ignorable by spec
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async _onrequest(msg) {
|
|
105
|
+
const { id, method } = msg
|
|
106
|
+
const handler = this._requestHandlers.get(method)
|
|
107
|
+
if (!handler) return this._reply({ jsonrpc: '2.0', id, error: { code: METHOD_NOT_FOUND, message: 'Method not found' } })
|
|
108
|
+
if (method === 'tools/call' && (!msg.params || typeof msg.params !== 'object' || typeof msg.params.name !== 'string')) {
|
|
109
|
+
return this._reply({ jsonrpc: '2.0', id, error: { code: INVALID_PARAMS, message: 'tools/call needs params.name' } })
|
|
110
|
+
}
|
|
111
|
+
const ac = new AbortController()
|
|
112
|
+
this._inflight.set(id, ac)
|
|
113
|
+
let reply
|
|
114
|
+
try {
|
|
115
|
+
const result = await handler({ method, params: msg.params }, { signal: ac.signal, requestId: id })
|
|
116
|
+
reply = { jsonrpc: '2.0', id, result: result === undefined ? {} : result }
|
|
117
|
+
} catch (e) {
|
|
118
|
+
const code = e && Number.isSafeInteger(e.code) ? e.code : INTERNAL_ERROR
|
|
119
|
+
reply = { jsonrpc: '2.0', id, error: { code, message: (e && e.message) || 'Internal error' } }
|
|
120
|
+
} finally {
|
|
121
|
+
this._inflight.delete(id)
|
|
122
|
+
}
|
|
123
|
+
if (ac.signal.aborted) return // cancelled by the client: it is no longer waiting for this
|
|
124
|
+
this._reply(reply)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
_reply(message) {
|
|
128
|
+
if (!this._transport) return
|
|
129
|
+
Promise.resolve(this._transport.send(message)).catch((e) => process.stderr.write(`[hive-stdio] could not reply: ${e && e.message}\n`))
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export class StdioServerTransport {
|
|
134
|
+
constructor(stdin = process.stdin, stdout = process.stdout) {
|
|
135
|
+
this._stdin = stdin
|
|
136
|
+
this._stdout = stdout
|
|
137
|
+
this._buf = ''
|
|
138
|
+
this._closed = false
|
|
139
|
+
this.onmessage = null
|
|
140
|
+
this.onclose = null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async start() {
|
|
144
|
+
this._stdin.setEncoding('utf8') // a multi-byte character split across two chunks stays whole
|
|
145
|
+
this._stdin.on('data', (chunk) => {
|
|
146
|
+
this._buf += chunk
|
|
147
|
+
let i
|
|
148
|
+
while ((i = this._buf.indexOf('\n')) >= 0) {
|
|
149
|
+
const line = this._buf.slice(0, i).replace(/\r$/, '')
|
|
150
|
+
this._buf = this._buf.slice(i + 1)
|
|
151
|
+
if (!line.trim()) continue
|
|
152
|
+
let msg
|
|
153
|
+
try { msg = JSON.parse(line) } catch {
|
|
154
|
+
process.stderr.write(`[hive-stdio] ignored a line that is not JSON (${line.length} chars)\n`)
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
try { if (this.onmessage) this.onmessage(msg) } catch (e) { process.stderr.write(`[hive-stdio] ${e && e.stack}\n`) }
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
const close = () => { if (this._closed) return; this._closed = true; if (this.onclose) this.onclose() }
|
|
161
|
+
this._stdin.on('end', close)
|
|
162
|
+
this._stdin.on('close', close)
|
|
163
|
+
this._stdin.on('error', close)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
send(message) {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
if (this._closed && !this._stdout.writable) return reject(new Error('stdout is closed'))
|
|
169
|
+
this._stdout.write(JSON.stringify(message) + '\n', (err) => (err ? reject(err) : resolve()))
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
}
|