toolfunnel 0.7.0 → 0.7.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.
Files changed (67) hide show
  1. package/README.md +228 -235
  2. package/SECURITY.md +84 -84
  3. package/bin/toolfunnel.js +451 -451
  4. package/docs/KNOWN_BUGS.md +80 -80
  5. package/docs/design.md +84 -84
  6. package/docs/packaging.md +115 -115
  7. package/hooks/hooks.manifest.json +27 -27
  8. package/hooks/scripts/example-audit.js +21 -21
  9. package/hooks/scripts/example-deny-dangerous.js +39 -39
  10. package/mcp/expose.example.json +30 -30
  11. package/mcp/servers/mock-modern/server.js +147 -147
  12. package/mcp/servers/mock-upstream/server.js +380 -380
  13. package/package.json +67 -67
  14. package/src/auth/config.js +174 -174
  15. package/src/auth/install.js +181 -181
  16. package/src/auth/resource-server.js +353 -353
  17. package/src/core/child-reaper.js +61 -61
  18. package/src/core/config-home.js +134 -134
  19. package/src/core/events.js +105 -105
  20. package/src/core/hook-engine.js +329 -329
  21. package/src/core/hook-loader.js +824 -824
  22. package/src/core/hook-runner.js +566 -566
  23. package/src/core/logger.js +297 -297
  24. package/src/core/matcher.js +100 -100
  25. package/src/core/metrics.js +75 -75
  26. package/src/core/rename-migration.js +163 -163
  27. package/src/core/requires.js +138 -138
  28. package/src/core/server-config.js +98 -98
  29. package/src/extend/add-hook.md +152 -152
  30. package/src/extend/add-mcp.md +119 -119
  31. package/src/extend/configure.md +88 -88
  32. package/src/extend/create-tool.md +168 -168
  33. package/src/extend/howto.js +102 -102
  34. package/src/extend/package.md +190 -190
  35. package/src/extend/wrap.md +99 -99
  36. package/src/mcp/aggregator.js +1719 -1674
  37. package/src/mcp/expose-store.js +577 -577
  38. package/src/mcp/gated-run.js +210 -210
  39. package/src/mcp/http-transport.js +1563 -1563
  40. package/src/mcp/mcp-client.js +1070 -1070
  41. package/src/mcp/modern.js +406 -406
  42. package/src/mcp/protocol.js +468 -468
  43. package/src/mcp/server.js +3112 -3112
  44. package/src/tools/registry.js +661 -661
  45. package/src/tools/tool-state.js +170 -170
  46. package/src/ui/public/app.js +1187 -1187
  47. package/src/ui/public/index.html +286 -286
  48. package/src/ui/public/styles.css +535 -535
  49. package/src/ui/server.js +1616 -1616
  50. package/tools/scripts/base64.js +139 -139
  51. package/tools/scripts/danger.js +61 -61
  52. package/tools/scripts/echo.js +39 -39
  53. package/tools/scripts/hash.js +101 -101
  54. package/tools/scripts/json.js +109 -109
  55. package/tools/scripts/text-stats.js +131 -131
  56. package/tools/scripts/tf-env.js +43 -43
  57. package/tools/scripts/tf-hook-add.js +151 -151
  58. package/tools/scripts/tf-hook-set.js +120 -120
  59. package/tools/scripts/tf-list.js +192 -192
  60. package/tools/scripts/tf-log.js +86 -86
  61. package/tools/scripts/tf-mcp-add.js +117 -117
  62. package/tools/scripts/tf-mcp-set.js +121 -121
  63. package/tools/scripts/tf-pack.js +237 -237
  64. package/tools/scripts/tf-tool-add.js +134 -134
  65. package/tools/scripts/tf-tool-set.js +142 -142
  66. package/tools/scripts/tf-wrap.js +103 -103
  67. package/tools/scripts/uuid.js +102 -102
package/SECURITY.md CHANGED
@@ -1,84 +1,84 @@
1
- # Security Policy
2
-
3
- ToolFunnel sits between MCP clients and MCP servers - it gates, filters, and forwards
4
- tool calls. That makes its security surface worth taking seriously, and reports are
5
- genuinely welcome.
6
-
7
- ## Reporting a vulnerability
8
-
9
- **Please use GitHub's private vulnerability reporting:** go to the
10
- [Security tab](https://github.com/Rendeverance/toolfunnel/security) → **Report a
11
- vulnerability**. That opens a private thread with the maintainer - nothing is public
12
- until a fix is out.
13
-
14
- Please **do not** open a public issue for a security-sensitive report. If you're unsure
15
- whether something is security-relevant, please err on the side of the private route.
16
-
17
- ## What counts
18
-
19
- Anything that lets a client or upstream server do what the configuration says it
20
- shouldn't. For example:
21
-
22
- - Bypassing the tool gate / hidden-tool filtering (`tf_tool_set`, expose config)
23
- - Auth bypass in the HTTP transport or OAuth resource-server validation
24
- - Token or credential leakage (in logs, error messages, or forwarded traffic)
25
- - Escaping the PreToolUse deny-hook
26
- - Injection via tool names, schemas, or forwarded arguments
27
-
28
- Bugs that crash ToolFunnel but don't cross a trust boundary are ordinary bugs -
29
- public issues are fine for those.
30
-
31
- ## What to expect
32
-
33
- This is a single-maintainer project. Reports get a response on a best-effort basis -
34
- normally within a few days. Confirmed vulnerabilities are fixed as a priority, and
35
- you'll be credited in the release notes unless you'd rather not be.
36
-
37
- ## Supported versions
38
-
39
- Only the latest published release (`npm install toolfunnel`) is supported with
40
- security fixes.
41
-
42
- ## Trust model
43
-
44
- **The config home is the trust boundary.** Anyone who can write the config home
45
- (`tools/`, `mcp/`, `hooks/`, `auth/`, `toolfunnel.json`) can define register tools, hooks,
46
- and upstream commands that execute with the gateway process's privileges. Protect it with
47
- filesystem permissions like you would a crontab or a shell profile.
48
-
49
- Everything else follows from that line:
50
-
51
- - **Upstream MCP servers are operator-chosen code.** Attaching or wrapping a server means
52
- trusting it with whatever its command can reach. In funnel mode the gateway *refuses*
53
- upstream arguments, working directories, and code-loading environment variables that
54
- reference paths outside the config home. The per-upstream opt-outs are explicit, and each
55
- is wider than a single check: `allowOutsidePaths` lifts the outside-path refusal for
56
- everything path-shaped that upstream carries - its arguments (including the value after
57
- `=` in a `--flag=value` form), the working directory its bare arguments resolve against,
58
- and its path-list environment variables; `allowCodeLoadingEnv` permits the code-loading
59
- environment class (`NODE_OPTIONS`, `NODE_PATH`, and their equivalents for other runtimes)
60
- on its own. Wrap mode permits outside paths with a security notice, because serving your
61
- real files is usually the point of a wrapped server. The guard is a tripwire for configs that quietly reach
62
- outside an auditable config home - not a sandbox against an untrusted pack author:
63
- installing a pack is installing software.
64
- - **The network surface binds loopback by default** (HTTP, SSE, and the web UI), validates
65
- the `Origin` header against loopback origins, and enforces the protocol-version
66
- and parameter-mirroring header rules of the 2026-07-28 revision. Exposing the port beyond
67
- localhost is an operator decision; put OAuth 2.1 (opt-in) or your own reverse proxy in
68
- front of it if you do.
69
- - **Zero runtime dependencies** - the supply-chain audit surface is this repository's own
70
- code (OAuth, if enabled, adds exactly one audited library: `jose`).
71
- - **An unreadable gate decision is a deny.** A hook that floods its output past the cap, whose
72
- decision JSON cannot be parsed, or whose decision slot carries a value outside the exact
73
- protocol vocabulary (a miscased `"DENY"`, a synonym like `"denied"`, the right word in the
74
- wrong slot, a wrong type) blocks the call it was gating rather than waving it through. A hook
75
- matcher that does not compile is the same class: it is refused at authoring, and one already
76
- in the manifest denies every tool-bearing call of its event until fixed. A `null` in a
77
- protocol slot is NOT an attempt - it reads as "no value", exactly like an absent key, and
78
- falls through. The deliberate exception: a hook script that CRASHES, is MISSING, or HANGS
79
- past its timeout is a non-blocking error and the call proceeds - Claude Code hook parity, so
80
- a script-execution failure cannot take every tool offline. An authored-config defect (the
81
- matcher, the decision vocabulary) gets no such grace: the author wrote a gate the runner
82
- cannot read, and it denies until corrected. Denies are `exit 2`; a gate that cannot reach a
83
- decision should exit 2 explicitly rather than error out. Pinned by
84
- `test/gate-semantics.test.js` and `test/matcher-compile.test.js`.
1
+ # Security Policy
2
+
3
+ ToolFunnel sits between MCP clients and MCP servers - it gates, filters, and forwards
4
+ tool calls. That makes its security surface worth taking seriously, and reports are
5
+ genuinely welcome.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ **Please use GitHub's private vulnerability reporting:** go to the
10
+ [Security tab](https://github.com/Rendeverance/toolfunnel/security) → **Report a
11
+ vulnerability**. That opens a private thread with the maintainer - nothing is public
12
+ until a fix is out.
13
+
14
+ Please **do not** open a public issue for a security-sensitive report. If you're unsure
15
+ whether something is security-relevant, please err on the side of the private route.
16
+
17
+ ## What counts
18
+
19
+ Anything that lets a client or upstream server do what the configuration says it
20
+ shouldn't. For example:
21
+
22
+ - Bypassing the tool gate / hidden-tool filtering (`tf_tool_set`, expose config)
23
+ - Auth bypass in the HTTP transport or OAuth resource-server validation
24
+ - Token or credential leakage (in logs, error messages, or forwarded traffic)
25
+ - Escaping the PreToolUse deny-hook
26
+ - Injection via tool names, schemas, or forwarded arguments
27
+
28
+ Bugs that crash ToolFunnel but don't cross a trust boundary are ordinary bugs -
29
+ public issues are fine for those.
30
+
31
+ ## What to expect
32
+
33
+ This is a single-maintainer project. Reports get a response on a best-effort basis -
34
+ normally within a few days. Confirmed vulnerabilities are fixed as a priority, and
35
+ you'll be credited in the release notes unless you'd rather not be.
36
+
37
+ ## Supported versions
38
+
39
+ Only the latest published release (`npm install toolfunnel`) is supported with
40
+ security fixes.
41
+
42
+ ## Trust model
43
+
44
+ **The config home is the trust boundary.** Anyone who can write the config home
45
+ (`tools/`, `mcp/`, `hooks/`, `auth/`, `toolfunnel.json`) can define register tools, hooks,
46
+ and upstream commands that execute with the gateway process's privileges. Protect it with
47
+ filesystem permissions like you would a crontab or a shell profile.
48
+
49
+ Everything else follows from that line:
50
+
51
+ - **Upstream MCP servers are operator-chosen code.** Attaching or wrapping a server means
52
+ trusting it with whatever its command can reach. In funnel mode the gateway *refuses*
53
+ upstream arguments, working directories, and code-loading environment variables that
54
+ reference paths outside the config home. The per-upstream opt-outs are explicit, and each
55
+ is wider than a single check: `allowOutsidePaths` lifts the outside-path refusal for
56
+ everything path-shaped that upstream carries - its arguments (including the value after
57
+ `=` in a `--flag=value` form), the working directory its bare arguments resolve against,
58
+ and its path-list environment variables; `allowCodeLoadingEnv` permits the code-loading
59
+ environment class (`NODE_OPTIONS`, `NODE_PATH`, and their equivalents for other runtimes)
60
+ on its own. Wrap mode permits outside paths with a security notice, because serving your
61
+ real files is usually the point of a wrapped server. The guard is a tripwire for configs that quietly reach
62
+ outside an auditable config home - not a sandbox against an untrusted pack author:
63
+ installing a pack is installing software.
64
+ - **The network surface binds loopback by default** (HTTP, SSE, and the web UI), validates
65
+ the `Origin` header against loopback origins, and enforces the protocol-version
66
+ and parameter-mirroring header rules of the 2026-07-28 revision. Exposing the port beyond
67
+ localhost is an operator decision; put OAuth 2.1 (opt-in) or your own reverse proxy in
68
+ front of it if you do.
69
+ - **Zero runtime dependencies** - the supply-chain audit surface is this repository's own
70
+ code (OAuth, if enabled, adds exactly one audited library: `jose`).
71
+ - **An unreadable gate decision is a deny.** A hook that floods its output past the cap, whose
72
+ decision JSON cannot be parsed, or whose decision slot carries a value outside the exact
73
+ protocol vocabulary (a miscased `"DENY"`, a synonym like `"denied"`, the right word in the
74
+ wrong slot, a wrong type) blocks the call it was gating rather than waving it through. A hook
75
+ matcher that does not compile is the same class: it is refused at authoring, and one already
76
+ in the manifest denies every tool-bearing call of its event until fixed. A `null` in a
77
+ protocol slot is NOT an attempt - it reads as "no value", exactly like an absent key, and
78
+ falls through. The deliberate exception: a hook script that CRASHES, is MISSING, or HANGS
79
+ past its timeout is a non-blocking error and the call proceeds - Claude Code hook parity, so
80
+ a script-execution failure cannot take every tool offline. An authored-config defect (the
81
+ matcher, the decision vocabulary) gets no such grace: the author wrote a gate the runner
82
+ cannot read, and it denies until corrected. Denies are `exit 2`; a gate that cannot reach a
83
+ decision should exit 2 explicitly rather than error out. Pinned by
84
+ `test/gate-semantics.test.js` and `test/matcher-compile.test.js`.