@rapidthoughtlabs/heku 0.3.0 → 0.3.1
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 +361 -62
- package/dist/cli.js +105 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# heku
|
|
2
2
|
|
|
3
|
-
> One server. Any
|
|
3
|
+
> One server. Any API. Any LLM.
|
|
4
4
|
|
|
5
5
|
**heku** is a single dynamic [Model Context Protocol](https://modelcontextprotocol.io) server that turns JSON config files into working API tools. No code to write — drop a config, and your LLM gets the tools instantly.
|
|
6
6
|
|
|
@@ -8,13 +8,21 @@ Stop building one MCP server per API. Build one config.
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## Try it now
|
|
12
|
+
|
|
13
|
+
**[console.rapidthoughtlabs.space](https://console.rapidthoughtlabs.space)** — hosted console you can point at any running heku instance. Connect, browse configs, chat with your tools, and inspect the system prompt — no local build needed.
|
|
14
|
+
|
|
15
|
+
**[app.rapidthoughtlabs.space](https://app.rapidthoughtlabs.space)** — **heku hub**, the online registry for browsing, installing, and publishing heku configs. Find community-built connectors for GitHub, Slack, Linear, and more — or publish your own.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
11
19
|
## Features
|
|
12
20
|
|
|
13
|
-
- **
|
|
21
|
+
- **8 connector types** — 4 standard (HTTP, GraphQL, gRPC, child-MCP) + 4 experimental (CLI, File, SQL, MongoDB)
|
|
14
22
|
- **Hot-reload** — add or edit a config, tools update live without restart
|
|
15
23
|
- **Auto-discovery** — gRPC reflection, GraphQL introspection, and child MCP tool listing fill in tools automatically
|
|
16
24
|
- **Built-in console UI** — React dashboard for chat, config editing, and registry browsing
|
|
17
|
-
- **
|
|
25
|
+
- **heku hub** — publish and install community configs from [app.rapidthoughtlabs.space](https://app.rapidthoughtlabs.space)
|
|
18
26
|
- **Auth handled** — bearer, basic, API key, and OAuth2 with `.env`-based credential management
|
|
19
27
|
- **Self-managing** — the server can create and edit its own configs via internal tools
|
|
20
28
|
|
|
@@ -39,12 +47,13 @@ heku start
|
|
|
39
47
|
|
|
40
48
|
## Quick start
|
|
41
49
|
|
|
42
|
-
Create
|
|
50
|
+
Create `mcp-configs/mcp.github.json`:
|
|
43
51
|
|
|
44
52
|
```json
|
|
45
53
|
{
|
|
46
|
-
"id": "github",
|
|
54
|
+
"id": "github-http",
|
|
47
55
|
"name": "GitHub API",
|
|
56
|
+
"description": "Manage GitHub repos, issues, and pull requests",
|
|
48
57
|
"connector": {
|
|
49
58
|
"type": "http",
|
|
50
59
|
"base_url": "https://api.github.com",
|
|
@@ -64,33 +73,327 @@ Create a config in `mcp-configs/mcp.github.json`:
|
|
|
64
73
|
}
|
|
65
74
|
```
|
|
66
75
|
|
|
67
|
-
Set your token:
|
|
68
|
-
|
|
69
76
|
```bash
|
|
70
|
-
heku auth setup github
|
|
77
|
+
heku auth setup github-http # writes GITHUB_TOKEN to .env
|
|
78
|
+
heku start
|
|
71
79
|
```
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
Your LLM now has a `github-http.list_repos` tool.
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Connectors
|
|
86
|
+
|
|
87
|
+
Tool names follow the pattern `config_id.tool_name` — e.g. `github-http.list_repos`, `linear-graphql.create_issue`.
|
|
88
|
+
|
|
89
|
+
### Standard
|
|
90
|
+
|
|
91
|
+
#### `http` — REST API
|
|
92
|
+
|
|
93
|
+
Define each endpoint as a tool. Supports `path`, `query`, `body`, and `header` params.
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"id": "stripe-http",
|
|
98
|
+
"name": "Stripe",
|
|
99
|
+
"connector": {
|
|
100
|
+
"type": "http",
|
|
101
|
+
"base_url": "https://api.stripe.com/v1",
|
|
102
|
+
"auth": { "type": "bearer", "token_env": "STRIPE_API_KEY" }
|
|
103
|
+
},
|
|
104
|
+
"tools": [
|
|
105
|
+
{
|
|
106
|
+
"name": "list_customers",
|
|
107
|
+
"description": "List Stripe customers with optional filters",
|
|
108
|
+
"method": "GET",
|
|
109
|
+
"path": "/customers",
|
|
110
|
+
"params": [
|
|
111
|
+
{ "name": "limit", "type": "number", "required": false, "location": "query", "description": "Max results (1–100)" },
|
|
112
|
+
{ "name": "email", "type": "string", "required": false, "location": "query", "description": "Filter by email address" }
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"name": "create_customer",
|
|
117
|
+
"description": "Create a new Stripe customer",
|
|
118
|
+
"method": "POST",
|
|
119
|
+
"path": "/customers",
|
|
120
|
+
"params": [
|
|
121
|
+
{ "name": "email", "type": "string", "required": true, "location": "body", "description": "Customer email" },
|
|
122
|
+
{ "name": "name", "type": "string", "required": false, "location": "body", "description": "Full name" }
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Tool fields:** `name`, `description`, `method` (`GET`/`POST`/`PUT`/`PATCH`/`DELETE`), `path` (supports `{{param}}` placeholders), `params[]`, `body_template?`, `response_map?`, `error_map?`
|
|
130
|
+
|
|
131
|
+
**Param locations:** `path` · `query` · `body` · `header`
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
#### `graphql` — GraphQL API
|
|
136
|
+
|
|
137
|
+
Tools are auto-discovered via introspection. Set `tools: []`.
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"id": "linear-graphql",
|
|
142
|
+
"name": "Linear",
|
|
143
|
+
"connector": {
|
|
144
|
+
"type": "graphql",
|
|
145
|
+
"endpoint": "https://api.linear.app/graphql",
|
|
146
|
+
"auth": { "type": "bearer", "token_env": "LINEAR_API_KEY" },
|
|
147
|
+
"include_mutations": true,
|
|
148
|
+
"include_queries": true
|
|
149
|
+
},
|
|
150
|
+
"tools": []
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Connector fields:** `endpoint`, `auth?`, `introspect?` (default `true`), `include_mutations?` (default `true`), `include_queries?` (default `true`), `headers?`, `timeout_ms?`
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
#### `grpc` — gRPC service
|
|
159
|
+
|
|
160
|
+
Tools are auto-discovered via server reflection or a `.proto` file. Set `tools: []`.
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"id": "myservice-grpc",
|
|
165
|
+
"name": "My gRPC Service",
|
|
166
|
+
"connector": {
|
|
167
|
+
"type": "grpc",
|
|
168
|
+
"endpoint": "localhost:50051",
|
|
169
|
+
"reflection": true,
|
|
170
|
+
"tls": false
|
|
171
|
+
},
|
|
172
|
+
"tools": []
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Or with a proto file:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"connector": {
|
|
181
|
+
"type": "grpc",
|
|
182
|
+
"endpoint": "grpc.example.com:443",
|
|
183
|
+
"proto_path": "./protos/service.proto",
|
|
184
|
+
"tls": true,
|
|
185
|
+
"auth": { "type": "bearer", "token_env": "GRPC_TOKEN" }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Connector fields:** `endpoint`, `reflection?` or `proto_path?` (one required), `tls?` (`true`/`false` or cert object), `auth?`, `metadata?`, `service_filter?`, `timeout_ms?`
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
#### `mcp` — child MCP server
|
|
195
|
+
|
|
196
|
+
Spawn any existing MCP server (stdio or SSE) and proxy its tools through heku. Tools are auto-discovered. Set `tools: []`.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"id": "filesystem-mcp",
|
|
201
|
+
"name": "Filesystem MCP",
|
|
202
|
+
"connector": {
|
|
203
|
+
"type": "mcp",
|
|
204
|
+
"transport": "stdio",
|
|
205
|
+
"command": "npx",
|
|
206
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
207
|
+
"install_command": "npm",
|
|
208
|
+
"install_args": ["install", "-g", "@modelcontextprotocol/server-filesystem"]
|
|
209
|
+
},
|
|
210
|
+
"tools": []
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
SSE transport:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"connector": {
|
|
219
|
+
"type": "mcp",
|
|
220
|
+
"transport": "sse",
|
|
221
|
+
"url": "http://localhost:8080/sse"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Connector fields:** `transport` (`stdio`/`sse`), `command?` + `args?` + `env?` (stdio), `url?` (sse), `install_command?`, `install_args?`, `install_cwd?`, `install_env?`, `install_timeout_ms?`, `active?`
|
|
227
|
+
|
|
228
|
+
> **Note:** `mcp` configs cannot be published to the registry — they reference local processes.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
### Experimental
|
|
233
|
+
|
|
234
|
+
> These connector types are functional but their config schema and behaviour may change in future releases.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
#### `cli` — shell command
|
|
239
|
+
|
|
240
|
+
Wrap any CLI tool as an MCP tool. Use `args_template` for positional args or `stdin_template` to pipe input.
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"id": "git-cli",
|
|
245
|
+
"name": "Git",
|
|
246
|
+
"connector": { "type": "cli" },
|
|
247
|
+
"tools": [
|
|
248
|
+
{
|
|
249
|
+
"name": "log",
|
|
250
|
+
"description": "Show recent git commits",
|
|
251
|
+
"args_template": ["git", "log", "--oneline", "-{{limit}}"],
|
|
252
|
+
"params": [
|
|
253
|
+
{ "name": "limit", "type": "number", "required": false, "description": "Number of commits to show" }
|
|
254
|
+
],
|
|
255
|
+
"output_as": "text"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"name": "diff",
|
|
259
|
+
"description": "Show unstaged changes",
|
|
260
|
+
"command": "git diff",
|
|
261
|
+
"params": [],
|
|
262
|
+
"output_as": "text"
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Tool fields:** `name`, `description`, `params[]`, `command?` (string) or `args_template?` (array), `stdin_template?`, `output_as?` (`"text"` | `"json"`)
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
#### `file` — filesystem
|
|
273
|
+
|
|
274
|
+
Read, write, append, delete, or list files. `path_template` supports `{{param}}` placeholders.
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"id": "notes-file",
|
|
279
|
+
"name": "Notes",
|
|
280
|
+
"connector": { "type": "file" },
|
|
281
|
+
"tools": [
|
|
282
|
+
{
|
|
283
|
+
"name": "read_note",
|
|
284
|
+
"description": "Read a note by name",
|
|
285
|
+
"operation": "read",
|
|
286
|
+
"path_template": "/home/user/notes/{{name}}.md",
|
|
287
|
+
"params": [
|
|
288
|
+
{ "name": "name", "type": "string", "required": true, "description": "Note filename without extension" }
|
|
289
|
+
]
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"name": "save_note",
|
|
293
|
+
"description": "Save or overwrite a note",
|
|
294
|
+
"operation": "write",
|
|
295
|
+
"path_template": "/home/user/notes/{{name}}.md",
|
|
296
|
+
"content_template": "{{content}}",
|
|
297
|
+
"params": [
|
|
298
|
+
{ "name": "name", "type": "string", "required": true, "description": "Note filename without extension" },
|
|
299
|
+
{ "name": "content", "type": "string", "required": true, "description": "Note content" }
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
]
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Tool fields:** `name`, `description`, `params[]`, `operation` (`read`/`write`/`append`/`delete`/`list`), `path_template`, `content_template?` (required for `write`/`append`)
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
#### `sql` — relational database
|
|
311
|
+
|
|
312
|
+
Named SQL queries with `:param` placeholders. Supports PostgreSQL, MySQL, and SQLite.
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"id": "analytics-sql",
|
|
317
|
+
"name": "Analytics DB",
|
|
318
|
+
"connector": {
|
|
319
|
+
"type": "sql",
|
|
320
|
+
"dialect": "postgres",
|
|
321
|
+
"connection_string_env": "DATABASE_URL"
|
|
322
|
+
},
|
|
323
|
+
"tools": [
|
|
324
|
+
{
|
|
325
|
+
"name": "active_users",
|
|
326
|
+
"description": "Count active users in a date range",
|
|
327
|
+
"sql": "SELECT COUNT(*) as count FROM users WHERE created_at BETWEEN :from AND :to AND active = true",
|
|
328
|
+
"params": [
|
|
329
|
+
{ "name": "from", "type": "string", "required": true, "description": "Start date (ISO 8601)" },
|
|
330
|
+
{ "name": "to", "type": "string", "required": true, "description": "End date (ISO 8601)" }
|
|
331
|
+
],
|
|
332
|
+
"max_rows": 1
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
}
|
|
77
336
|
```
|
|
78
337
|
|
|
79
|
-
|
|
338
|
+
**Connector fields:** `dialect` (`postgres`/`mysql`/`sqlite`), `connection_string_env?` or field-based (`host`, `port`, `database`, `username_env`, `password_env`), `ssl?`, `pool_max?`
|
|
339
|
+
|
|
340
|
+
**Tool fields:** `name`, `description`, `params[]`, `sql` (`:name` placeholders only — no `{{}}`, no `?`, no `$N`), `max_rows?` (1–10000), `timeout_ms?`
|
|
80
341
|
|
|
81
342
|
---
|
|
82
343
|
|
|
83
|
-
|
|
344
|
+
#### `mongodb` — MongoDB
|
|
345
|
+
|
|
346
|
+
Document operations with JSON templates. Placeholders use `{{param}}` in templates.
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"id": "catalog-mongo",
|
|
351
|
+
"name": "Product Catalog",
|
|
352
|
+
"connector": {
|
|
353
|
+
"type": "mongodb",
|
|
354
|
+
"database": "catalog",
|
|
355
|
+
"connection_string_env": "MONGO_URI"
|
|
356
|
+
},
|
|
357
|
+
"tools": [
|
|
358
|
+
{
|
|
359
|
+
"name": "find_products",
|
|
360
|
+
"description": "Search products by category and price range",
|
|
361
|
+
"collection": "products",
|
|
362
|
+
"operation": "find",
|
|
363
|
+
"filter_template": { "category": "{{category}}", "price": { "$lte": "{{max_price}}" } },
|
|
364
|
+
"params": [
|
|
365
|
+
{ "name": "category", "type": "string", "required": true, "description": "Product category" },
|
|
366
|
+
{ "name": "max_price", "type": "number", "required": false, "description": "Maximum price" }
|
|
367
|
+
],
|
|
368
|
+
"max_rows": 50
|
|
369
|
+
}
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**Connector fields:** `database`, `connection_string_env?` or `host?`+`port?`, `auth_source?`, `tls?`
|
|
375
|
+
|
|
376
|
+
**Tool fields:** `name`, `description`, `params[]`, `collection`, `operation` (`find`/`findOne`/`aggregate`/`insertOne`/`insertMany`/`updateOne`/`updateMany`/`deleteOne`/`deleteMany`/`countDocuments`/`distinct`), plus operation-specific templates: `filter_template`, `update_template`, `document_template`, `documents_template`, `pipeline_template`, `projection?`, `sort?`, `max_rows?`, `limit?`, `timeout_ms?`
|
|
377
|
+
|
|
378
|
+
---
|
|
84
379
|
|
|
85
|
-
|
|
380
|
+
## Auth types
|
|
381
|
+
|
|
382
|
+
All credentials read from environment variables — `heku auth setup` writes them to `.env`:
|
|
383
|
+
|
|
384
|
+
| Type | Header |
|
|
86
385
|
|---|---|
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
386
|
+
| `bearer` | `Authorization: Bearer {token}` |
|
|
387
|
+
| `basic` | `Authorization: Basic base64(user:token)` |
|
|
388
|
+
| `api_key` | Custom header, e.g. `X-API-Key` |
|
|
389
|
+
| `oauth2_static` | Pre-acquired OAuth2 access token |
|
|
390
|
+
|
|
391
|
+
```json
|
|
392
|
+
{ "type": "bearer", "token_env": "GITHUB_TOKEN" }
|
|
393
|
+
{ "type": "api_key", "key_env": "MY_KEY", "header_name": "X-Api-Key" }
|
|
394
|
+
{ "type": "basic", "username_env": "MY_USER", "token_env": "MY_PASS" }
|
|
395
|
+
{ "type": "oauth2_static","token_env": "MY_OAUTH_TOKEN" }
|
|
396
|
+
```
|
|
94
397
|
|
|
95
398
|
---
|
|
96
399
|
|
|
@@ -119,43 +422,17 @@ heku update Update heku to the latest version
|
|
|
119
422
|
heku help Show usage
|
|
120
423
|
```
|
|
121
424
|
|
|
122
|
-
|
|
425
|
+
Start with the console UI:
|
|
123
426
|
|
|
124
427
|
```bash
|
|
125
428
|
heku start --http --port 3456
|
|
126
429
|
```
|
|
127
430
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
## Configuration
|
|
131
|
-
|
|
132
|
-
### Config file shape
|
|
133
|
-
|
|
134
|
-
Configs live in `mcp-configs/mcp.{id}.json`:
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
{
|
|
138
|
-
id: string; // becomes the tool namespace prefix
|
|
139
|
-
name: string;
|
|
140
|
-
description?: string; // shown to the LLM
|
|
141
|
-
connector: ConnectorConfig; // one of 7 types
|
|
142
|
-
tools: ToolDef[]; // empty for auto-discovery (grpc/graphql/mcp)
|
|
143
|
-
overlays?: { // override tool descriptions without editing the config
|
|
144
|
-
[toolName: string]: { description?: string }
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Auth types
|
|
150
|
-
|
|
151
|
-
All credentials read from environment variables — `heku auth setup` writes them to `.env`:
|
|
431
|
+
Then open **[console.rapidthoughtlabs.space](https://console.rapidthoughtlabs.space)** and connect to `http://localhost:3456`.
|
|
152
432
|
|
|
153
|
-
|
|
154
|
-
- **`basic`** — base64(username:token)
|
|
155
|
-
- **`api_key`** — custom header (e.g. `X-API-Key`)
|
|
156
|
-
- **`oauth2_static`** — pre-acquired OAuth2 access token
|
|
433
|
+
---
|
|
157
434
|
|
|
158
|
-
|
|
435
|
+
## System config (optional)
|
|
159
436
|
|
|
160
437
|
Drop `heku.config.json` in your config directory:
|
|
161
438
|
|
|
@@ -163,7 +440,7 @@ Drop `heku.config.json` in your config directory:
|
|
|
163
440
|
{
|
|
164
441
|
"log_level": "info",
|
|
165
442
|
"rate_limits": {
|
|
166
|
-
"github": { "requests_per_minute": 60 }
|
|
443
|
+
"github-http": { "requests_per_minute": 60 }
|
|
167
444
|
},
|
|
168
445
|
"self_config": true
|
|
169
446
|
}
|
|
@@ -173,28 +450,27 @@ Drop `heku.config.json` in your config directory:
|
|
|
173
450
|
|
|
174
451
|
## Console UI
|
|
175
452
|
|
|
176
|
-
The dashboard is a React + Vite app
|
|
453
|
+
The dashboard is a React + Vite app — available hosted at **[console.rapidthoughtlabs.space](https://console.rapidthoughtlabs.space)** or embedded when you run `heku start --http`.
|
|
177
454
|
|
|
178
|
-
- **Chat** — test tools through a model of your choice
|
|
455
|
+
- **Chat** — test tools through a model of your choice (OpenAI, Together AI)
|
|
179
456
|
- **Configs** — visual editor for connector and tool definitions
|
|
457
|
+
- **Prompts** — inspect the system prompt layers and token counts
|
|
180
458
|
- **Registry** — browse, install, and publish configs
|
|
181
|
-
- **Auth** —
|
|
182
|
-
|
|
183
|
-
Built with React 19, TailwindCSS v4, Zustand, and the MCP SDK.
|
|
459
|
+
- **Auth** — credential status across all configs at a glance
|
|
184
460
|
|
|
185
461
|
---
|
|
186
462
|
|
|
187
|
-
##
|
|
463
|
+
## heku hub
|
|
188
464
|
|
|
189
|
-
[
|
|
465
|
+
**[app.rapidthoughtlabs.space](https://app.rapidthoughtlabs.space)** is the default registry for sharing configs — browse community connectors, install with one command, and publish your own.
|
|
190
466
|
|
|
191
467
|
```bash
|
|
192
468
|
heku install @rtl/github
|
|
193
469
|
heku install @rtl/slack@1.2.0
|
|
194
|
-
heku publish
|
|
470
|
+
heku publish mcp-configs/mcp.stripe-http.json
|
|
195
471
|
```
|
|
196
472
|
|
|
197
|
-
Use `--registry
|
|
473
|
+
Use `--registry <url>` to point at a self-hosted registry.
|
|
198
474
|
|
|
199
475
|
---
|
|
200
476
|
|
|
@@ -227,7 +503,30 @@ mcp-configs/ Local config files (gitignored)
|
|
|
227
503
|
|
|
228
504
|
## Tech stack
|
|
229
505
|
|
|
230
|
-
TypeScript · Node.js (ESM) · `@modelcontextprotocol/sdk` · Express · React 19 · Vite ·
|
|
506
|
+
TypeScript · Node.js (ESM) · `@modelcontextprotocol/sdk` · Express · React 19 · Vite · Zustand · `@grpc/grpc-js` · GraphQL · tsup · Vitest
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Changelog
|
|
511
|
+
|
|
512
|
+
### 0.3.1
|
|
513
|
+
- Renamed meta-tool namespace from `mcp.one.*` to `heku.*` across all connectors, prompts, and client code
|
|
514
|
+
- Fixed deployed console manifest style switcher (settings API calls now use the correct bridge base URL)
|
|
515
|
+
- Fixed prompt page config catalog not refreshing when heku connects after page load
|
|
516
|
+
- Markdown rendering in the demo chat — assistant responses now render formatted text
|
|
517
|
+
- Config catalog descriptions now show in composed prompt preview; falls back to display name when description is absent
|
|
518
|
+
- Dual manifest preview in Prompts page — flat and namespaced styles with separate token counts
|
|
519
|
+
- heku server version now reads from `package.json` at runtime in dev mode instead of showing `0.0.0-dev`
|
|
520
|
+
|
|
521
|
+
### 0.3.0
|
|
522
|
+
- Registry versioning overhaul — semver-based publish/install flow
|
|
523
|
+
- CLI registry commands: `install`, `uninstall`, `fork`, `publish`
|
|
524
|
+
- Console registry browser tab
|
|
525
|
+
|
|
526
|
+
### 0.2.x
|
|
527
|
+
- SQL and MongoDB connector types (experimental)
|
|
528
|
+
- Config write lock — block LLM agents from mutating configs
|
|
529
|
+
- Hot-reload watcher improvements
|
|
231
530
|
|
|
232
531
|
---
|
|
233
532
|
|
package/dist/cli.js
CHANGED
|
@@ -26827,10 +26827,10 @@ var require_view = __commonJS({
|
|
|
26827
26827
|
var debug = require_src()("express:view");
|
|
26828
26828
|
var path24 = __require("path");
|
|
26829
26829
|
var fs23 = __require("fs");
|
|
26830
|
-
var
|
|
26830
|
+
var dirname4 = path24.dirname;
|
|
26831
26831
|
var basename3 = path24.basename;
|
|
26832
26832
|
var extname2 = path24.extname;
|
|
26833
|
-
var
|
|
26833
|
+
var join4 = path24.join;
|
|
26834
26834
|
var resolve3 = path24.resolve;
|
|
26835
26835
|
module2.exports = View;
|
|
26836
26836
|
function View(name, options) {
|
|
@@ -26866,7 +26866,7 @@ var require_view = __commonJS({
|
|
|
26866
26866
|
for (var i = 0; i < roots.length && !path25; i++) {
|
|
26867
26867
|
var root = roots[i];
|
|
26868
26868
|
var loc = resolve3(root, name);
|
|
26869
|
-
var dir =
|
|
26869
|
+
var dir = dirname4(loc);
|
|
26870
26870
|
var file2 = basename3(loc);
|
|
26871
26871
|
path25 = this.resolve(dir, file2);
|
|
26872
26872
|
}
|
|
@@ -26892,12 +26892,12 @@ var require_view = __commonJS({
|
|
|
26892
26892
|
};
|
|
26893
26893
|
View.prototype.resolve = function resolve4(dir, file2) {
|
|
26894
26894
|
var ext = this.ext;
|
|
26895
|
-
var path25 =
|
|
26895
|
+
var path25 = join4(dir, file2);
|
|
26896
26896
|
var stat4 = tryStat(path25);
|
|
26897
26897
|
if (stat4 && stat4.isFile()) {
|
|
26898
26898
|
return path25;
|
|
26899
26899
|
}
|
|
26900
|
-
path25 =
|
|
26900
|
+
path25 = join4(dir, basename3(file2, ext), "index" + ext);
|
|
26901
26901
|
stat4 = tryStat(path25);
|
|
26902
26902
|
if (stat4 && stat4.isFile()) {
|
|
26903
26903
|
return path25;
|
|
@@ -30573,7 +30573,7 @@ var require_send = __commonJS({
|
|
|
30573
30573
|
var Stream = __require("stream");
|
|
30574
30574
|
var util2 = __require("util");
|
|
30575
30575
|
var extname2 = path24.extname;
|
|
30576
|
-
var
|
|
30576
|
+
var join4 = path24.join;
|
|
30577
30577
|
var normalize2 = path24.normalize;
|
|
30578
30578
|
var resolve3 = path24.resolve;
|
|
30579
30579
|
var sep = path24.sep;
|
|
@@ -30745,7 +30745,7 @@ var require_send = __commonJS({
|
|
|
30745
30745
|
return res;
|
|
30746
30746
|
}
|
|
30747
30747
|
parts = path25.split(sep);
|
|
30748
|
-
path25 = normalize2(
|
|
30748
|
+
path25 = normalize2(join4(root, path25));
|
|
30749
30749
|
} else {
|
|
30750
30750
|
if (UP_PATH_REGEXP.test(path25)) {
|
|
30751
30751
|
debug('malicious path "%s"', path25);
|
|
@@ -30878,7 +30878,7 @@ var require_send = __commonJS({
|
|
|
30878
30878
|
if (err) return self2.onStatError(err);
|
|
30879
30879
|
return self2.error(404);
|
|
30880
30880
|
}
|
|
30881
|
-
var p =
|
|
30881
|
+
var p = join4(path25, self2._index[i]);
|
|
30882
30882
|
debug('stat "%s"', p);
|
|
30883
30883
|
fs23.stat(p, function(err2, stat4) {
|
|
30884
30884
|
if (err2) return next(err2);
|
|
@@ -79142,7 +79142,7 @@ var require_named_placeholders = __commonJS({
|
|
|
79142
79142
|
}
|
|
79143
79143
|
return s;
|
|
79144
79144
|
}
|
|
79145
|
-
function
|
|
79145
|
+
function join4(tree) {
|
|
79146
79146
|
if (tree.length === 1) {
|
|
79147
79147
|
return tree;
|
|
79148
79148
|
}
|
|
@@ -79168,7 +79168,7 @@ var require_named_placeholders = __commonJS({
|
|
|
79168
79168
|
if (cache2 && (tree = cache2.get(query))) {
|
|
79169
79169
|
return toArrayParams(tree, paramsObj);
|
|
79170
79170
|
}
|
|
79171
|
-
tree =
|
|
79171
|
+
tree = join4(parse3(query));
|
|
79172
79172
|
if (cache2) {
|
|
79173
79173
|
cache2.set(query, tree);
|
|
79174
79174
|
}
|
|
@@ -81773,9 +81773,9 @@ var require_bindings = __commonJS({
|
|
|
81773
81773
|
init_esm_shims();
|
|
81774
81774
|
var fs23 = __require("fs");
|
|
81775
81775
|
var path24 = __require("path");
|
|
81776
|
-
var
|
|
81777
|
-
var
|
|
81778
|
-
var
|
|
81776
|
+
var fileURLToPath4 = require_file_uri_to_path();
|
|
81777
|
+
var join4 = path24.join;
|
|
81778
|
+
var dirname4 = path24.dirname;
|
|
81779
81779
|
var exists = fs23.accessSync && function(path25) {
|
|
81780
81780
|
try {
|
|
81781
81781
|
fs23.accessSync(path25);
|
|
@@ -81834,7 +81834,7 @@ var require_bindings = __commonJS({
|
|
|
81834
81834
|
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
81835
81835
|
var tries = [], i = 0, l = opts.try.length, n, b, err;
|
|
81836
81836
|
for (; i < l; i++) {
|
|
81837
|
-
n =
|
|
81837
|
+
n = join4.apply(
|
|
81838
81838
|
null,
|
|
81839
81839
|
opts.try[i].map(function(p) {
|
|
81840
81840
|
return opts[p] || p;
|
|
@@ -81885,17 +81885,17 @@ var require_bindings = __commonJS({
|
|
|
81885
81885
|
Error.stackTraceLimit = origSTL;
|
|
81886
81886
|
var fileSchema = "file://";
|
|
81887
81887
|
if (fileName.indexOf(fileSchema) === 0) {
|
|
81888
|
-
fileName =
|
|
81888
|
+
fileName = fileURLToPath4(fileName);
|
|
81889
81889
|
}
|
|
81890
81890
|
return fileName;
|
|
81891
81891
|
};
|
|
81892
81892
|
exports2.getRoot = function getRoot(file2) {
|
|
81893
|
-
var dir =
|
|
81893
|
+
var dir = dirname4(file2), prev;
|
|
81894
81894
|
while (true) {
|
|
81895
81895
|
if (dir === ".") {
|
|
81896
81896
|
dir = process.cwd();
|
|
81897
81897
|
}
|
|
81898
|
-
if (exists(
|
|
81898
|
+
if (exists(join4(dir, "package.json")) || exists(join4(dir, "node_modules"))) {
|
|
81899
81899
|
return dir;
|
|
81900
81900
|
}
|
|
81901
81901
|
if (prev === dir) {
|
|
@@ -81904,7 +81904,7 @@ var require_bindings = __commonJS({
|
|
|
81904
81904
|
);
|
|
81905
81905
|
}
|
|
81906
81906
|
prev = dir;
|
|
81907
|
-
dir =
|
|
81907
|
+
dir = join4(dir, "..");
|
|
81908
81908
|
}
|
|
81909
81909
|
};
|
|
81910
81910
|
}
|
|
@@ -130795,7 +130795,7 @@ function isConnectorType(value) {
|
|
|
130795
130795
|
// src/lib/config-rules.ts
|
|
130796
130796
|
init_esm_shims();
|
|
130797
130797
|
init_env_store();
|
|
130798
|
-
var RESERVED_IDS = ["
|
|
130798
|
+
var RESERVED_IDS = ["heku"];
|
|
130799
130799
|
function validateBaseId(id) {
|
|
130800
130800
|
if (!id || typeof id !== "string" || id.trim() === "") {
|
|
130801
130801
|
return "id must be a non-empty string";
|
|
@@ -131059,7 +131059,10 @@ function getInstalledEntry(slug, registry2 = "default") {
|
|
|
131059
131059
|
|
|
131060
131060
|
// src/lib/version.ts
|
|
131061
131061
|
init_esm_shims();
|
|
131062
|
-
|
|
131062
|
+
import { readFileSync } from "fs";
|
|
131063
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
131064
|
+
import { join, dirname } from "path";
|
|
131065
|
+
var VERSION = true ? "0.3.1" : readPkgVersion();
|
|
131063
131066
|
|
|
131064
131067
|
// src/connectors/mcp.ts
|
|
131065
131068
|
init_esm_shims();
|
|
@@ -133923,8 +133926,40 @@ var serverSettings = {
|
|
|
133923
133926
|
function getServerSettings() {
|
|
133924
133927
|
return { ...serverSettings };
|
|
133925
133928
|
}
|
|
133929
|
+
var DISCOVERY_FLAT_SET = /* @__PURE__ */ new Set(["heku.search", "heku.list_tools", "heku.list_configs", "heku.invoke"]);
|
|
133930
|
+
var DISCOVERY_TRIO_SET = /* @__PURE__ */ new Set(["heku.search", "heku.list_tools", "heku.list_configs"]);
|
|
133926
133931
|
function createAdminRouter(ctx) {
|
|
133927
133932
|
const router = (0, import_express.Router)();
|
|
133933
|
+
router.get("/tools-manifest", (req, res) => {
|
|
133934
|
+
const styleParam = req.query.style;
|
|
133935
|
+
const style = styleParam === "namespaced" || styleParam === "flat" ? styleParam : serverSettings.manifestStyle;
|
|
133936
|
+
const tools = ctx.registry.list();
|
|
133937
|
+
if (style === "namespaced") {
|
|
133938
|
+
const visible2 = tools.filter(
|
|
133939
|
+
(rt) => DISCOVERY_TRIO_SET.has(`${rt.configId}.${rt.tool.name}`)
|
|
133940
|
+
);
|
|
133941
|
+
res.json(
|
|
133942
|
+
visible2.map((rt) => ({
|
|
133943
|
+
name: `${rt.configId}.${rt.tool.name}`,
|
|
133944
|
+
description: rt.tool.description,
|
|
133945
|
+
inputSchema: buildInputSchema2(rt.tool.params ?? []),
|
|
133946
|
+
configId: rt.configId
|
|
133947
|
+
}))
|
|
133948
|
+
);
|
|
133949
|
+
return;
|
|
133950
|
+
}
|
|
133951
|
+
const visible = tools.filter(
|
|
133952
|
+
(rt) => DISCOVERY_FLAT_SET.has(`${rt.configId}.${rt.tool.name}`)
|
|
133953
|
+
);
|
|
133954
|
+
res.json(
|
|
133955
|
+
visible.map((rt) => ({
|
|
133956
|
+
name: rt.tool.name,
|
|
133957
|
+
description: rt.tool.description,
|
|
133958
|
+
inputSchema: buildInputSchema2(rt.tool.params ?? []),
|
|
133959
|
+
configId: rt.configId
|
|
133960
|
+
}))
|
|
133961
|
+
);
|
|
133962
|
+
});
|
|
133928
133963
|
router.get("/server-settings", (_req, res) => {
|
|
133929
133964
|
res.json({
|
|
133930
133965
|
hotReload: ctx.watcher ? !ctx.watcher.isPaused() : serverSettings.hotReload,
|
|
@@ -134350,8 +134385,8 @@ var ToolRegistry = class {
|
|
|
134350
134385
|
return this.tools.size;
|
|
134351
134386
|
}
|
|
134352
134387
|
};
|
|
134353
|
-
var DISCOVERY_FLAT = /* @__PURE__ */ new Set(["
|
|
134354
|
-
var DISCOVERY_TRIO = /* @__PURE__ */ new Set(["
|
|
134388
|
+
var DISCOVERY_FLAT = /* @__PURE__ */ new Set(["heku.search", "heku.list_tools", "heku.list_configs", "heku.invoke"]);
|
|
134389
|
+
var DISCOVERY_TRIO = /* @__PURE__ */ new Set(["heku.search", "heku.list_tools", "heku.list_configs"]);
|
|
134355
134390
|
function buildManifestView(style, registry2) {
|
|
134356
134391
|
if (style === "namespaced") {
|
|
134357
134392
|
return {
|
|
@@ -134362,24 +134397,24 @@ function buildManifestView(style, registry2) {
|
|
|
134362
134397
|
}
|
|
134363
134398
|
return {
|
|
134364
134399
|
visible: registry2.list().filter((rt) => DISCOVERY_FLAT.has(`${rt.configId}.${rt.tool.name}`)),
|
|
134365
|
-
rewrite: (qualified) => qualified.replace(/^
|
|
134366
|
-
resolve: (incoming) => incoming.includes(".") ? incoming : `
|
|
134400
|
+
rewrite: (qualified) => qualified.replace(/^heku\./, ""),
|
|
134401
|
+
resolve: (incoming) => incoming.includes(".") ? incoming : `heku.${incoming}`
|
|
134367
134402
|
};
|
|
134368
134403
|
}
|
|
134369
134404
|
var BLOCKED_WHEN_LOCKED = /* @__PURE__ */ new Set([
|
|
134370
|
-
"
|
|
134371
|
-
"
|
|
134372
|
-
"
|
|
134373
|
-
"
|
|
134374
|
-
"
|
|
134375
|
-
"
|
|
134376
|
-
"
|
|
134377
|
-
"
|
|
134378
|
-
"
|
|
134405
|
+
"heku.create_config",
|
|
134406
|
+
"heku.update_config",
|
|
134407
|
+
"heku.delete_config",
|
|
134408
|
+
"heku.add_tool",
|
|
134409
|
+
"heku.remove_tool",
|
|
134410
|
+
"heku.update_tool",
|
|
134411
|
+
"heku.registry_install",
|
|
134412
|
+
"heku.registry_update",
|
|
134413
|
+
"heku.auth_set"
|
|
134379
134414
|
]);
|
|
134380
134415
|
function blockedTool(qualifiedName, args2) {
|
|
134381
134416
|
if (BLOCKED_WHEN_LOCKED.has(qualifiedName)) return qualifiedName;
|
|
134382
|
-
if (qualifiedName === "
|
|
134417
|
+
if (qualifiedName === "heku.invoke") {
|
|
134383
134418
|
const target = args2["tool"];
|
|
134384
134419
|
if (typeof target === "string" && BLOCKED_WHEN_LOCKED.has(target)) return target;
|
|
134385
134420
|
}
|
|
@@ -135373,9 +135408,9 @@ var NodeFsHandler = class {
|
|
|
135373
135408
|
if (this.fsw.closed) {
|
|
135374
135409
|
return;
|
|
135375
135410
|
}
|
|
135376
|
-
const
|
|
135411
|
+
const dirname4 = sp.dirname(file2);
|
|
135377
135412
|
const basename3 = sp.basename(file2);
|
|
135378
|
-
const parent = this.fsw._getWatchedDir(
|
|
135413
|
+
const parent = this.fsw._getWatchedDir(dirname4);
|
|
135379
135414
|
let prevStats = stats;
|
|
135380
135415
|
if (parent.has(basename3))
|
|
135381
135416
|
return;
|
|
@@ -135402,7 +135437,7 @@ var NodeFsHandler = class {
|
|
|
135402
135437
|
prevStats = newStats2;
|
|
135403
135438
|
}
|
|
135404
135439
|
} catch (error3) {
|
|
135405
|
-
this.fsw._remove(
|
|
135440
|
+
this.fsw._remove(dirname4, basename3);
|
|
135406
135441
|
}
|
|
135407
135442
|
} else if (parent.has(basename3)) {
|
|
135408
135443
|
const at = newStats.atimeMs;
|
|
@@ -136365,7 +136400,7 @@ init_loader();
|
|
|
136365
136400
|
var DEBOUNCE_MS = 300;
|
|
136366
136401
|
function isMcpConfigFile(filePath) {
|
|
136367
136402
|
const base2 = path11.basename(filePath);
|
|
136368
|
-
return base2.startsWith("mcp.") && base2.endsWith(".json") && base2 !== "mcp.
|
|
136403
|
+
return base2.startsWith("mcp.") && base2.endsWith(".json") && base2 !== "mcp.heku.json";
|
|
136369
136404
|
}
|
|
136370
136405
|
function isMcpEnvFile(filePath) {
|
|
136371
136406
|
const base2 = path11.basename(filePath);
|
|
@@ -136687,7 +136722,7 @@ function resolveConfigDir(cliOverride, systemConfig) {
|
|
|
136687
136722
|
init_esm_shims();
|
|
136688
136723
|
var import_express5 = __toESM(require_express2(), 1);
|
|
136689
136724
|
var import_cors = __toESM(require_lib3(), 1);
|
|
136690
|
-
import { fileURLToPath as
|
|
136725
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
136691
136726
|
|
|
136692
136727
|
// server/mcp-client.ts
|
|
136693
136728
|
init_esm_shims();
|
|
@@ -137614,7 +137649,21 @@ function createApiRouter(mcp) {
|
|
|
137614
137649
|
}
|
|
137615
137650
|
}
|
|
137616
137651
|
});
|
|
137617
|
-
router.get("/tools/manifest", (
|
|
137652
|
+
router.get("/tools/manifest", async (req, res) => {
|
|
137653
|
+
const style = req.query.style;
|
|
137654
|
+
if (style === "flat" || style === "namespaced") {
|
|
137655
|
+
try {
|
|
137656
|
+
const tools = await admin.get(`/tools-manifest?style=${style}`);
|
|
137657
|
+
res.json(tools);
|
|
137658
|
+
} catch (err) {
|
|
137659
|
+
if (err instanceof AdminUnavailableError) {
|
|
137660
|
+
res.json([]);
|
|
137661
|
+
return;
|
|
137662
|
+
}
|
|
137663
|
+
res.status(500).json({ error: err.message });
|
|
137664
|
+
}
|
|
137665
|
+
return;
|
|
137666
|
+
}
|
|
137618
137667
|
res.json(mcp.listTools());
|
|
137619
137668
|
});
|
|
137620
137669
|
router.post("/tools/call", async (req, res) => {
|
|
@@ -138163,7 +138212,7 @@ async function startBridge(options) {
|
|
|
138163
138212
|
}
|
|
138164
138213
|
};
|
|
138165
138214
|
}
|
|
138166
|
-
var isMain = process.argv[1] ===
|
|
138215
|
+
var isMain = process.argv[1] === fileURLToPath3(import.meta.url);
|
|
138167
138216
|
if (isMain) {
|
|
138168
138217
|
const port = Number(process.env["PORT"] ?? 3456);
|
|
138169
138218
|
const bridge = await startBridge({ port });
|
|
@@ -138191,7 +138240,7 @@ init_env_store();
|
|
|
138191
138240
|
// src/internal-config.ts
|
|
138192
138241
|
init_esm_shims();
|
|
138193
138242
|
var INTERNAL_CONFIG = {
|
|
138194
|
-
id: "
|
|
138243
|
+
id: "heku",
|
|
138195
138244
|
name: "heku Self-Management",
|
|
138196
138245
|
description: "Create configs, add tools, install from registry, manage auth \u2014 heku's own management interface for LLM agents.",
|
|
138197
138246
|
connector: { type: "internal" },
|
|
@@ -138222,7 +138271,7 @@ var INTERNAL_CONFIG = {
|
|
|
138222
138271
|
},
|
|
138223
138272
|
{
|
|
138224
138273
|
name: "search",
|
|
138225
|
-
description: "Find tools by name or intent across all configs (or within a specific one). Returns matching tools with full schemas grouped by quality: exact \u2192 partial \u2192 description \u2192 related. Covers both native `
|
|
138274
|
+
description: "Find tools by name or intent across all configs (or within a specific one). Returns matching tools with full schemas grouped by quality: exact \u2192 partial \u2192 description \u2192 related. Covers both native `heku.*` self-management tools and all loaded service tools. Narrow by config: `github-http` returns only github-http tools; `github` matches github-http and github-cli. Once you have a tool name, call `invoke` with `config_id.tool_name` to execute it.",
|
|
138226
138275
|
params: [
|
|
138227
138276
|
{
|
|
138228
138277
|
name: "query",
|
|
@@ -138240,13 +138289,13 @@ var INTERNAL_CONFIG = {
|
|
|
138240
138289
|
},
|
|
138241
138290
|
{
|
|
138242
138291
|
name: "invoke",
|
|
138243
|
-
description: "Execute any registered tool by its qualified name (`config_id.tool_name`). Workflow: (1) list_configs \u2192 pick a config id, (2) search or list_tools \u2192 get the tool name and required args, (3) invoke \u2192 run it. Examples: `github-http.create_issue`, `linear-graphql.create_issue`, `
|
|
138292
|
+
description: "Execute any registered tool by its qualified name (`config_id.tool_name`). Workflow: (1) list_configs \u2192 pick a config id, (2) search or list_tools \u2192 get the tool name and required args, (3) invoke \u2192 run it. Examples: `github-http.create_issue`, `linear-graphql.create_issue`, `heku.server_status`.",
|
|
138244
138293
|
params: [
|
|
138245
138294
|
{
|
|
138246
138295
|
name: "tool",
|
|
138247
138296
|
type: "string",
|
|
138248
138297
|
required: true,
|
|
138249
|
-
description: "Qualified tool name in the format config_id.tool_name (e.g. 'open-meteo-http.get_forecast', 'github-http.create_issue', '
|
|
138298
|
+
description: "Qualified tool name in the format config_id.tool_name (e.g. 'open-meteo-http.get_forecast', 'github-http.create_issue', 'heku.server_status')"
|
|
138250
138299
|
},
|
|
138251
138300
|
{
|
|
138252
138301
|
name: "args",
|
|
@@ -138268,7 +138317,7 @@ var INTERNAL_CONFIG = {
|
|
|
138268
138317
|
},
|
|
138269
138318
|
{
|
|
138270
138319
|
name: "delete_config",
|
|
138271
|
-
description: "Delete a config file and unregister all its tools. Cannot delete the '
|
|
138320
|
+
description: "Delete a config file and unregister all its tools. Cannot delete the 'heku' self-management config \u2014 use self_config: false in heku.config.json instead.",
|
|
138272
138321
|
params: [
|
|
138273
138322
|
{ name: "config_id", type: "string", required: true, description: "Config ID to delete" }
|
|
138274
138323
|
]
|
|
@@ -138307,9 +138356,9 @@ var INTERNAL_CONFIG = {
|
|
|
138307
138356
|
},
|
|
138308
138357
|
{
|
|
138309
138358
|
name: "list_tools",
|
|
138310
|
-
description: "Returns tools with full schemas ready to call. No args: returns only the native `
|
|
138359
|
+
description: "Returns tools with full schemas ready to call. No args: returns only the native `heku.*` self-management surface. With config_id: returns all tools in that config. For targeted lookup by name or intent, use `heku.search` instead.",
|
|
138311
138360
|
params: [
|
|
138312
|
-
{ name: "config_id", type: "string", required: false, description: "Config ID to list tools for. Omit to list only native
|
|
138361
|
+
{ name: "config_id", type: "string", required: false, description: "Config ID to list tools for. Omit to list only native heku.* tools." }
|
|
138313
138362
|
]
|
|
138314
138363
|
},
|
|
138315
138364
|
{
|
|
@@ -141441,7 +141490,7 @@ async function handleUpdateTool(ctx, args2) {
|
|
|
141441
141490
|
}
|
|
141442
141491
|
async function handleListTools(ctx, args2) {
|
|
141443
141492
|
const filterById = args2.config_id;
|
|
141444
|
-
const scopeId = filterById ?? "
|
|
141493
|
+
const scopeId = filterById ?? "heku";
|
|
141445
141494
|
const tools = ctx.registry.list().filter((rt) => rt.configId === scopeId).map((rt) => ({
|
|
141446
141495
|
qualified_name: `${rt.configId}.${rt.tool.name}`,
|
|
141447
141496
|
config_id: rt.configId,
|
|
@@ -142009,7 +142058,7 @@ async function handleInvoke(ctx, args2) {
|
|
|
142009
142058
|
success: false,
|
|
142010
142059
|
data: {
|
|
142011
142060
|
error: `Tool "${toolName}" not found`,
|
|
142012
|
-
hint: "Use
|
|
142061
|
+
hint: "Use heku.search or heku.list_tools to find available tool names",
|
|
142013
142062
|
available_count: available.length
|
|
142014
142063
|
}
|
|
142015
142064
|
};
|
|
@@ -142555,8 +142604,8 @@ async function run(args2) {
|
|
|
142555
142604
|
}
|
|
142556
142605
|
log.info("server", `Loading configs from: ${configDir}`);
|
|
142557
142606
|
const handAuthored = loadConfigs(configDir).filter((c) => {
|
|
142558
|
-
if (c.id === "
|
|
142559
|
-
log.warn("server", `mcp.
|
|
142607
|
+
if (c.id === "heku") {
|
|
142608
|
+
log.warn("server", `mcp.heku.json in ${configDir} is ignored \u2014 internal config is built-in`);
|
|
142560
142609
|
return false;
|
|
142561
142610
|
}
|
|
142562
142611
|
return true;
|
|
@@ -142566,7 +142615,7 @@ async function run(args2) {
|
|
|
142566
142615
|
...handAuthored
|
|
142567
142616
|
];
|
|
142568
142617
|
if (handAuthored.length === 0) {
|
|
142569
|
-
log.info("server", `No user configs yet \u2014 drop mcp.*.json files in ${configDir} or call
|
|
142618
|
+
log.info("server", `No user configs yet \u2014 drop mcp.*.json files in ${configDir} or call heku.registry_install`);
|
|
142570
142619
|
}
|
|
142571
142620
|
for (const config2 of allConfigs) {
|
|
142572
142621
|
if (config2.connector.type === "mcp") {
|
|
@@ -142699,13 +142748,13 @@ async function run(args2) {
|
|
|
142699
142748
|
}
|
|
142700
142749
|
if (systemConfig.self_config === false) {
|
|
142701
142750
|
const before = allConfigs.length;
|
|
142702
|
-
allConfigs.splice(0, allConfigs.length, ...allConfigs.filter((c) => c.id !== "
|
|
142751
|
+
allConfigs.splice(0, allConfigs.length, ...allConfigs.filter((c) => c.id !== "heku"));
|
|
142703
142752
|
if (allConfigs.length < before) {
|
|
142704
142753
|
log.info("server", "self_config: false \u2014 heku self-management disabled");
|
|
142705
142754
|
}
|
|
142706
142755
|
} else if (systemConfig.self_config && typeof systemConfig.self_config === "object") {
|
|
142707
142756
|
const sc = systemConfig.self_config;
|
|
142708
|
-
const oneConfig = allConfigs.find((c) => c.id === "
|
|
142757
|
+
const oneConfig = allConfigs.find((c) => c.id === "heku");
|
|
142709
142758
|
if (oneConfig) {
|
|
142710
142759
|
if (sc.allow) {
|
|
142711
142760
|
oneConfig.tools = oneConfig.tools.filter((t) => sc.allow.includes(t.name));
|
|
@@ -143494,7 +143543,7 @@ async function run5(args2) {
|
|
|
143494
143543
|
} else {
|
|
143495
143544
|
let candidates = [];
|
|
143496
143545
|
if (fs18.existsSync(configDir)) {
|
|
143497
|
-
candidates = fs18.readdirSync(configDir).filter((f) => f.startsWith("mcp.") && f.endsWith(".json") && f !== "mcp.
|
|
143546
|
+
candidates = fs18.readdirSync(configDir).filter((f) => f.startsWith("mcp.") && f.endsWith(".json") && f !== "mcp.heku.json").map((f) => path20.join(configDir, f));
|
|
143498
143547
|
}
|
|
143499
143548
|
if (candidates.length === 0) {
|
|
143500
143549
|
console.error(red("\u2717") + ` No config file found. Try: ${bold("heku publish <name>")}`);
|