toolfunnel 0.3.0 → 0.5.0

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.
Files changed (40) hide show
  1. package/README.md +167 -324
  2. package/bin/toolfunnel.js +39 -4
  3. package/docs/KNOWN_BUGS.md +33 -0
  4. package/docs/MANUAL.pdf +0 -0
  5. package/docs/packaging.md +115 -0
  6. package/package.json +3 -1
  7. package/src/auth/config.js +4 -2
  8. package/src/auth/resource-server.js +28 -3
  9. package/src/core/config-home.js +134 -0
  10. package/src/core/logger.js +4 -2
  11. package/src/core/requires.js +138 -0
  12. package/src/core/server-config.js +77 -0
  13. package/src/extend/create-tool.md +24 -4
  14. package/src/extend/package.md +33 -0
  15. package/src/mcp/aggregator.js +12 -7
  16. package/src/mcp/http-transport.js +71 -21
  17. package/src/mcp/mcp-client.js +9 -3
  18. package/src/mcp/server.js +181 -16
  19. package/src/packages/packager.js +5 -3
  20. package/src/tools/registry.js +47 -2
  21. package/src/ui/server.js +49 -3
  22. package/tools/scripts/tf-env.js +43 -0
  23. package/tools/scripts/tf-hook-add.js +4 -5
  24. package/tools/scripts/tf-hook-set.js +4 -5
  25. package/tools/scripts/tf-list.js +9 -12
  26. package/tools/scripts/tf-log.js +4 -1
  27. package/tools/scripts/tf-mcp-add.js +4 -6
  28. package/tools/scripts/tf-mcp-set.js +4 -6
  29. package/tools/scripts/tf-pack.js +237 -0
  30. package/tools/scripts/tf-tool-add.js +16 -16
  31. package/tools/scripts/tf-tool-set.js +5 -11
  32. package/tools/tools.register.json +46 -1
  33. package/docs/admin-ui.md +0 -404
  34. package/docs/configuration.md +0 -402
  35. package/docs/hooks-and-gating.md +0 -428
  36. package/docs/logging.md +0 -223
  37. package/docs/management.md +0 -429
  38. package/docs/oauth.md +0 -103
  39. package/docs/setup.md +0 -352
  40. package/docs/tools-and-upstreams.md +0 -469
package/docs/setup.md DELETED
@@ -1,352 +0,0 @@
1
- # ToolFunnel — Setup
2
-
3
- How to install ToolFunnel and register it with any MCP-capable agent. ToolFunnel is one
4
- small MCP gateway: it hosts first-party tools, forwards and curates tools from upstream MCP
5
- servers, exposes them **leanly** (short briefs + schema-on-demand), and gates every
6
- execution through your own policy hooks.
7
-
8
- It speaks the same protocol two ways:
9
-
10
- - **stdio** (default) — the client spawns ToolFunnel as a child process and talks JSON-RPC
11
- over stdin/stdout. Simplest; nothing to leave running.
12
- - **HTTP/SSE** (`--http`) — a long-lived host process owns the server on `127.0.0.1:9998`
13
- and any number of clients connect over `http://127.0.0.1:9998/mcp`.
14
-
15
- Both serve the identical tool surface and run every call through the same gate.
16
-
17
- ---
18
-
19
- ## 1. Requirements
20
-
21
- - **Node.js >= 18.** That is the only requirement. (`node --version` to check.)
22
- - **Zero runtime dependencies.** ToolFunnel uses Node built-ins only — no npm packages, no
23
- MCP SDK. There is nothing to `npm install` for it to run, and nothing transitive to audit.
24
- - **Loopback only.** The HTTP host binds `127.0.0.1` and rejects non-loopback `Host` headers
25
- (DNS-rebinding guard). It is designed for a local agent on the same machine, not remote
26
- access.
27
-
28
- ---
29
-
30
- ## 2. Install
31
-
32
- Clone (or copy) the repository somewhere stable. Because there are no dependencies, that is
33
- the whole install:
34
-
35
- ```bash
36
- git clone <repo-url> toolfunnel
37
- cd toolfunnel
38
- node bin/toolfunnel.js --help # verify it runs
39
- ```
40
-
41
- You should see the usage banner. That confirms Node can run the entry point.
42
-
43
- > `npm install` is **optional and a no-op** here — `dependencies` is empty, so there is
44
- > nothing to fetch. Running it just writes a lockfile; ToolFunnel runs straight from a clone.
45
-
46
- ### Optional: a global `toolfunnel` command
47
-
48
- The package declares a `toolfunnel` bin, so you can put it on your `PATH` instead of typing
49
- the full path every time:
50
-
51
- ```bash
52
- # from inside the cloned repo
53
- npm link # symlinks a global `toolfunnel` to this checkout
54
- # …or
55
- npm install -g . # installs it globally
56
- ```
57
-
58
- After either, `toolfunnel` and `toolfunnel --http` work from anywhere. (`npx toolfunnel`
59
- will also work once the package is published to a registry.)
60
-
61
- Throughout this document, **pick one invocation form** and use it consistently in your
62
- client config:
63
-
64
- | Form | stdio command | HTTP launch |
65
- |------|---------------|-------------|
66
- | Direct (no install) | `node /abs/path/to/toolfunnel/bin/toolfunnel.js` | `node /abs/path/to/toolfunnel/bin/toolfunnel.js --http` |
67
- | Global install / link | `toolfunnel` | `toolfunnel --http` |
68
- | Published package | `npx toolfunnel` | `npx toolfunnel --http` |
69
-
70
- > **Windows paths in JSON:** use forward slashes (`F:/dev/toolfunnel/bin/toolfunnel.js`) or
71
- > escaped backslashes (`F:\\dev\\toolfunnel\\bin\\toolfunnel.js`). Node accepts forward
72
- > slashes on Windows, so forward slashes are the simplest choice.
73
-
74
- ---
75
-
76
- ## 3. Running
77
-
78
- ### stdio (default)
79
-
80
- ```bash
81
- node bin/toolfunnel.js
82
- # or, if installed globally:
83
- toolfunnel
84
- ```
85
-
86
- It reads JSON-RPC from stdin and writes responses to stdout (diagnostics go to stderr, so
87
- they never pollute the protocol channel). It accepts both `Content-Length`-framed messages
88
- and newline-delimited JSON. You normally don't run this by hand — the MCP client launches it
89
- for you (section 5). A quick smoke test:
90
-
91
- ```bash
92
- echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | node bin/toolfunnel.js
93
- ```
94
-
95
- You should get one JSON line back with `"serverInfo":{"name":"toolfunnel","version":"0.3.0"}`
96
- and `"protocolVersion":"2024-11-05"`.
97
-
98
- > **Working directory doesn't matter.** ToolFunnel resolves its config (`tools/`, `mcp/`,
99
- > `hooks/`) relative to its own install location, not the directory it's launched from. So a
100
- > client that spawns `node /abs/path/to/toolfunnel/bin/toolfunnel.js` from any `cwd` still
101
- > finds the right register, expose store, and hook manifest. You never need to set a `cwd` in
102
- > the client config.
103
-
104
- ### HTTP/SSE host
105
-
106
- ```bash
107
- node bin/toolfunnel.js --http
108
- # or:
109
- toolfunnel --http
110
- # npm script shortcut, from the repo:
111
- npm run http
112
- ```
113
-
114
- This starts a long-lived host and logs to stderr:
115
-
116
- ```
117
- [toolfunnel] HTTP MCP host listening on http://127.0.0.1:9998
118
- ```
119
-
120
- Endpoints:
121
-
122
- | Method + path | Purpose |
123
- |---------------|---------|
124
- | `POST /mcp` | One JSON-RPC request → one JSON-RPC response. This is the call channel. |
125
- | `GET /mcp` | Server→client SSE stream (`Accept: text/event-stream`) — the Streamable-HTTP standard. |
126
- | `GET /mcp/sse` | Working alias for the SSE stream (older HTTP+SSE shape; still supported). |
127
- | `GET /health` | Synchronous JSON health snapshot. |
128
-
129
- **The URL your client points at is `http://127.0.0.1:9998/mcp`** (note the `/mcp` path — the
130
- bare origin is not the endpoint).
131
-
132
- Bind options:
133
-
134
- ```bash
135
- toolfunnel --http --port 9000 # bind a specific port
136
- toolfunnel --http --port 0 # OS-assigned ephemeral port (read it from the stderr log)
137
- toolfunnel --http --host 127.0.0.1 # explicit loopback host (default)
138
- ```
139
-
140
- Stop it with Ctrl+C (SIGINT) — it shuts down cleanly.
141
-
142
- Smoke-test a running host:
143
-
144
- ```bash
145
- curl -s http://127.0.0.1:9998/health
146
- curl -s -X POST http://127.0.0.1:9998/mcp \
147
- -H "Content-Type: application/json" \
148
- -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
149
- ```
150
-
151
- The `tools/list` response lists the lean meta-tools the agent uses to discover everything
152
- else (see section 6).
153
-
154
- ---
155
-
156
- ## 4. stdio vs HTTP — which to register
157
-
158
- - **stdio** is the default and the simplest: zero processes to manage, the client starts and
159
- stops ToolFunnel with its own lifecycle. Best when one client owns the gateway.
160
- - **HTTP** is better when you want **one** gateway shared by several clients/sessions, want
161
- it to stay up across client restarts, or want to hit `/health` and the SSE
162
- `tools/list_changed` stream. You start the host yourself (or via a service manager) and
163
- point clients at the URL.
164
-
165
- Both expose the same tools and enforce the same gate, so you can switch freely.
166
-
167
- ---
168
-
169
- ## 5. Registering with clients
170
-
171
- ### Claude Code
172
-
173
- Claude Code reads MCP servers from a `.mcp.json` file (project root for project scope, or
174
- your user config). The top-level key is `mcpServers`.
175
-
176
- **stdio form** — Claude Code launches ToolFunnel itself:
177
-
178
- ```json
179
- {
180
- "mcpServers": {
181
- "toolfunnel": {
182
- "command": "node",
183
- "args": ["F:/dev/toolfunnel/bin/toolfunnel.js"]
184
- }
185
- }
186
- }
187
- ```
188
-
189
- If you installed the global bin, you can use it directly instead:
190
-
191
- ```json
192
- {
193
- "mcpServers": {
194
- "toolfunnel": {
195
- "command": "toolfunnel",
196
- "args": []
197
- }
198
- }
199
- }
200
- ```
201
-
202
- **HTTP form** — point at an already-running host (`toolfunnel --http`):
203
-
204
- ```json
205
- {
206
- "mcpServers": {
207
- "toolfunnel": {
208
- "type": "http",
209
- "url": "http://127.0.0.1:9998/mcp"
210
- }
211
- }
212
- }
213
- ```
214
-
215
- You can also add either form from the CLI instead of hand-editing the file:
216
-
217
- ```bash
218
- # stdio (everything after -- is the launch command)
219
- claude mcp add toolfunnel -- node F:/dev/toolfunnel/bin/toolfunnel.js
220
-
221
- # http (host must be running)
222
- claude mcp add --transport http toolfunnel http://127.0.0.1:9998/mcp
223
- ```
224
-
225
- ### Generic MCP clients
226
-
227
- Any MCP client follows one of the two shapes above. The vocabulary is near-universal:
228
-
229
- **stdio** — give the client a command + args; it spawns the process and speaks JSON-RPC over
230
- the pipes:
231
-
232
- ```json
233
- {
234
- "command": "node",
235
- "args": ["F:/dev/toolfunnel/bin/toolfunnel.js"]
236
- }
237
- ```
238
-
239
- **HTTP / Streamable-HTTP** — give the client the endpoint URL; it POSTs JSON-RPC to it and
240
- (optionally) opens the `GET /mcp` SSE stream:
241
-
242
- ```
243
- http://127.0.0.1:9998/mcp
244
- ```
245
-
246
- If a client distinguishes "HTTP" from the older "SSE" transport, ToolFunnel supports both on
247
- the same host: use `http://127.0.0.1:9998/mcp` for Streamable-HTTP, or
248
- `http://127.0.0.1:9998/mcp/sse` for the legacy SSE alias. Protocol version is `2024-11-05`.
249
-
250
- ### Cursor
251
-
252
- Cursor reads `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per project), also under a
253
- `mcpServers` key.
254
-
255
- **stdio:**
256
-
257
- ```json
258
- {
259
- "mcpServers": {
260
- "toolfunnel": {
261
- "command": "node",
262
- "args": ["F:/dev/toolfunnel/bin/toolfunnel.js"]
263
- }
264
- }
265
- }
266
- ```
267
-
268
- **HTTP** (point at a running `toolfunnel --http`; Cursor uses a `url` field):
269
-
270
- ```json
271
- {
272
- "mcpServers": {
273
- "toolfunnel": {
274
- "url": "http://127.0.0.1:9998/mcp"
275
- }
276
- }
277
- }
278
- ```
279
-
280
- ### Windsurf
281
-
282
- Windsurf reads `~/.codeium/windsurf/mcp_config.json`, again under `mcpServers`.
283
-
284
- **stdio:**
285
-
286
- ```json
287
- {
288
- "mcpServers": {
289
- "toolfunnel": {
290
- "command": "node",
291
- "args": ["F:/dev/toolfunnel/bin/toolfunnel.js"]
292
- }
293
- }
294
- }
295
- ```
296
-
297
- **HTTP** (Windsurf uses a `serverUrl` field for remote/HTTP servers):
298
-
299
- ```json
300
- {
301
- "mcpServers": {
302
- "toolfunnel": {
303
- "serverUrl": "http://127.0.0.1:9998/mcp"
304
- }
305
- }
306
- }
307
- ```
308
-
309
- > Field names for the HTTP/SSE URL vary by client (`url`, `serverUrl`, or a `type:"http"`
310
- > block). The URL itself is always `http://127.0.0.1:9998/mcp`. Check your client's MCP docs
311
- > if neither field name is accepted.
312
-
313
- ---
314
-
315
- ## 6. What you'll see after connecting
316
-
317
- ToolFunnel exposes a deliberately small surface so it costs almost no context. After
318
- connecting, the agent sees these meta-tools instead of every upstream schema:
319
-
320
- - **`toolfunnel_list_tools`** — list the available tools as short briefs (id, summary,
321
- category). This is the lean catalogue.
322
- - **`toolfunnel_tool_instructions`** — fetch the full instructions/schema for one tool, on
323
- demand. The agent only pays for the schema of a tool it's actually about to use.
324
- - **`toolfunnel_howto`** — self-extension guidance (adding tools, hooks, upstream MCPs).
325
-
326
- That's the token saver: the long tail of tools lives behind `toolfunnel_list_tools` rather
327
- than as hundreds of always-loaded `tools/list` entries.
328
-
329
- Out of the box there are 9 demo first-party tools (echo, base64, hash, uuid, json,
330
- text-stats, and a few inventory/diagnostic helpers) and **no** upstream MCPs connected
331
- (`expose.json` is empty by default). Add upstreams and curate which of their tools to expose
332
- via the configuration files — see the self-extension guides under `src/extend/`
333
- (`add-mcp.md`, `create-tool.md`, `add-hook.md`) or call `toolfunnel_howto`.
334
-
335
- ---
336
-
337
- ## 7. Troubleshooting
338
-
339
- - **Client shows no tools / connection fails (stdio):** run the exact `command` + `args`
340
- from your config in a terminal and pipe the initialize smoke-test from section 3. If that
341
- prints a `serverInfo` line, the launch command is correct; re-check the JSON path
342
- escaping in your config.
343
- - **`EADDRINUSE` on `--http`:** port 9998 is taken. Start with `--port <n>` (or `--port 0`
344
- for an OS-assigned port, then read the actual port from the stderr log line) and update
345
- the client URL to match.
346
- - **`403 forbidden: non-loopback Host`:** you reached the host with a non-loopback `Host`
347
- header (e.g. a real hostname or LAN IP). ToolFunnel is loopback-only by design — use
348
- `127.0.0.1` or `localhost`.
349
- - **Is it alive?** `curl -s http://127.0.0.1:9998/health` returns `{"ok":true,...}` with the
350
- bound `url`, protocol version, and tool counts when the host is up.
351
- - **Node too old:** `node --version` must be >= 18. Earlier versions lack APIs the transport
352
- relies on.