forage-mcp 0.1.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 (76) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +157 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +146 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/persistence/log.d.ts +11 -0
  9. package/dist/persistence/log.d.ts.map +1 -0
  10. package/dist/persistence/log.js +21 -0
  11. package/dist/persistence/log.js.map +1 -0
  12. package/dist/persistence/manifest.d.ts +24 -0
  13. package/dist/persistence/manifest.d.ts.map +1 -0
  14. package/dist/persistence/manifest.js +45 -0
  15. package/dist/persistence/manifest.js.map +1 -0
  16. package/dist/proxy/manager.d.ts +20 -0
  17. package/dist/proxy/manager.d.ts.map +1 -0
  18. package/dist/proxy/manager.js +77 -0
  19. package/dist/proxy/manager.js.map +1 -0
  20. package/dist/proxy/wrapper.d.ts +20 -0
  21. package/dist/proxy/wrapper.d.ts.map +1 -0
  22. package/dist/proxy/wrapper.js +37 -0
  23. package/dist/proxy/wrapper.js.map +1 -0
  24. package/dist/registries/npm.d.ts +10 -0
  25. package/dist/registries/npm.d.ts.map +1 -0
  26. package/dist/registries/npm.js +74 -0
  27. package/dist/registries/npm.js.map +1 -0
  28. package/dist/registries/official.d.ts +23 -0
  29. package/dist/registries/official.d.ts.map +1 -0
  30. package/dist/registries/official.js +57 -0
  31. package/dist/registries/official.js.map +1 -0
  32. package/dist/registries/smithery.d.ts +3 -0
  33. package/dist/registries/smithery.d.ts.map +1 -0
  34. package/dist/registries/smithery.js +27 -0
  35. package/dist/registries/smithery.js.map +1 -0
  36. package/dist/registries/types.d.ts +13 -0
  37. package/dist/registries/types.d.ts.map +1 -0
  38. package/dist/registries/types.js +2 -0
  39. package/dist/registries/types.js.map +1 -0
  40. package/dist/rules/detector.d.ts +8 -0
  41. package/dist/rules/detector.d.ts.map +1 -0
  42. package/dist/rules/detector.js +54 -0
  43. package/dist/rules/detector.js.map +1 -0
  44. package/dist/rules/writer.d.ts +8 -0
  45. package/dist/rules/writer.d.ts.map +1 -0
  46. package/dist/rules/writer.js +135 -0
  47. package/dist/rules/writer.js.map +1 -0
  48. package/dist/server.d.ts +3 -0
  49. package/dist/server.d.ts.map +1 -0
  50. package/dist/server.js +167 -0
  51. package/dist/server.js.map +1 -0
  52. package/dist/tools/evaluate.d.ts +17 -0
  53. package/dist/tools/evaluate.d.ts.map +1 -0
  54. package/dist/tools/evaluate.js +31 -0
  55. package/dist/tools/evaluate.js.map +1 -0
  56. package/dist/tools/install.d.ts +18 -0
  57. package/dist/tools/install.d.ts.map +1 -0
  58. package/dist/tools/install.js +91 -0
  59. package/dist/tools/install.js.map +1 -0
  60. package/dist/tools/learn.d.ts +12 -0
  61. package/dist/tools/learn.d.ts.map +1 -0
  62. package/dist/tools/learn.js +31 -0
  63. package/dist/tools/learn.js.map +1 -0
  64. package/dist/tools/search.d.ts +10 -0
  65. package/dist/tools/search.d.ts.map +1 -0
  66. package/dist/tools/search.js +50 -0
  67. package/dist/tools/search.js.map +1 -0
  68. package/dist/tools/status.d.ts +20 -0
  69. package/dist/tools/status.d.ts.map +1 -0
  70. package/dist/tools/status.js +24 -0
  71. package/dist/tools/status.js.map +1 -0
  72. package/dist/tools/uninstall.d.ts +11 -0
  73. package/dist/tools/uninstall.d.ts.map +1 -0
  74. package/dist/tools/uninstall.js +55 -0
  75. package/dist/tools/uninstall.js.map +1 -0
  76. package/package.json +49 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2025-02-16
9
+
10
+ ### Added
11
+
12
+ - MCP server with 6 tools: `forage_search`, `forage_evaluate`, `forage_install`, `forage_learn`, `forage_status`, `forage_uninstall`
13
+ - Registry search across Official MCP Registry, Smithery, and npm
14
+ - Proxy/gateway pattern — installed tools run as child processes with instant availability via `list_changed` notifications
15
+ - Persistence layer at `~/.forage/` with `manifest.json` (auto-start config) and `install-log.json` (audit trail)
16
+ - Agent rule file support — writes to CLAUDE.md, AGENTS.md, and `.cursor/rules/` with clean HTML comment markers
17
+ - CLI with `forage init`, `forage search`, and `forage list` commands
18
+ - Human approval required for all installs (`confirm: true` safety gate)
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Isaac Levine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # Forage
2
+
3
+ **Self-improving tool discovery for AI agents.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/forage-mcp.svg)](https://www.npmjs.com/package/forage-mcp)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+
8
+ Forage is an MCP server that lets AI agents discover, install, and learn to use new tools — automatically. When an agent hits a wall, it forages for the right tool, installs it, and teaches itself how to use it. The agent gets permanently smarter.
9
+
10
+ ## The Problem
11
+
12
+ AI coding agents are limited to whatever tools they're configured with at session start. When an agent needs to query a database, deploy to Vercel, or search Slack, it apologizes and the human manually installs the right MCP server. This is the bottleneck of agentic development.
13
+
14
+ ## The Solution
15
+
16
+ Forage closes the self-improvement loop:
17
+
18
+ ```
19
+ Agent encounters a task it can't do
20
+ → forage_search("query postgres database")
21
+ → forage_install("@modelcontextprotocol/server-postgres")
22
+ → Tools available IMMEDIATELY (no restart)
23
+ → forage_learn() saves instructions to CLAUDE.md
24
+ → Next session: auto-starts, agent already knows how to use it
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### Claude Code
30
+
31
+ ```bash
32
+ claude mcp add forage -- npx -y forage-mcp
33
+ ```
34
+
35
+ ### Cursor
36
+
37
+ ```bash
38
+ npx forage-mcp init --client cursor
39
+ ```
40
+
41
+ Then start a new session. Forage is ready.
42
+
43
+ ## Tools
44
+
45
+ | Tool | Description |
46
+ |---|---|
47
+ | `forage_search` | Search for MCP servers across the [Official MCP Registry](https://registry.modelcontextprotocol.io), [Smithery](https://smithery.ai), and [npm](https://www.npmjs.com) |
48
+ | `forage_evaluate` | Get details on a package — downloads, README, install command |
49
+ | `forage_install` | Install and start an MCP server as a proxied subprocess (requires user approval) |
50
+ | `forage_learn` | Write usage instructions to CLAUDE.md / AGENTS.md / .cursor/rules/ |
51
+ | `forage_status` | List all installed and running tools |
52
+ | `forage_uninstall` | Remove a tool and clean up rules |
53
+
54
+ ## How It Works
55
+
56
+ Forage is an MCP server that acts as a **gateway/proxy**:
57
+
58
+ 1. **You install Forage once** — it's the only MCP server you configure manually
59
+ 2. **Forage discovers tools** — searches the Official MCP Registry, Smithery, and npm in parallel
60
+ 3. **Forage installs tools** — starts them as child processes, wraps their capabilities
61
+ 4. **No restart needed** — Forage emits `list_changed` notifications, agent picks up new tools instantly
62
+ 5. **Knowledge persists** — `forage_learn` writes to agent rule files, manifest auto-starts tools next session
63
+
64
+ ### Architecture
65
+
66
+ ```
67
+ ┌─────────────────────────────────────────────┐
68
+ │ Claude Code / Cursor / Codex │
69
+ │ │
70
+ │ "I need to query a Postgres database" │
71
+ └──────────────────┬──────────────────────────┘
72
+ │ MCP
73
+
74
+ ┌─────────────────────────────────────────────┐
75
+ │ Forage MCP Server │
76
+ │ │
77
+ │ forage_search ─── Official Registry │
78
+ │ forage_install Smithery │
79
+ │ forage_learn npm │
80
+ │ forage_status │
81
+ │ │
82
+ │ ┌─────────────┐ ┌─────────────┐ │
83
+ │ │ Postgres MCP│ │ GitHub MCP │ ... │
84
+ │ │ (subprocess)│ │ (subprocess)│ │
85
+ │ └─────────────┘ └─────────────┘ │
86
+ └─────────────────────────────────────────────┘
87
+ ```
88
+
89
+ ### The Proxy Pattern
90
+
91
+ When you install a tool through Forage:
92
+
93
+ 1. Forage runs `npx -y <package>` as a child process
94
+ 2. Connects to it via `StdioClientTransport` (MCP client)
95
+ 3. Discovers the child server's tools via `listTools`
96
+ 4. Re-registers each tool on the Forage server with a namespaced name (`foraged__<server>__<tool>`)
97
+ 5. Sends `tools/list_changed` notification — the agent sees new tools immediately
98
+ 6. When the agent calls a proxied tool, Forage forwards the call to the child server
99
+
100
+ ## Persistence
101
+
102
+ Forage stores its state in `~/.forage/`:
103
+
104
+ | File | Purpose |
105
+ |---|---|
106
+ | `manifest.json` | Installed tools, command/args, auto-start configuration |
107
+ | `install-log.json` | Audit trail of all installs and uninstalls |
108
+ | `cache/` | Cached registry search results |
109
+
110
+ On startup, Forage reads the manifest and auto-starts all previously installed servers. Your agent picks up right where it left off.
111
+
112
+ ## CLI
113
+
114
+ Forage also includes a CLI for humans:
115
+
116
+ ```bash
117
+ forage search "postgres database" # Search registries
118
+ forage list # List installed tools
119
+ forage init # Set up for Claude Code
120
+ forage init --client cursor # Set up for Cursor
121
+ ```
122
+
123
+ ## Security
124
+
125
+ - **Human approval required** — `forage_install` always requires explicit `confirm: true`. The agent cannot install tools without the user approving the tool call.
126
+ - **Audit trail** — every install/uninstall is logged with timestamps to `~/.forage/install-log.json`
127
+ - **No remote backend** — everything runs locally. Registry searches are read-only GET requests to public APIs.
128
+ - **No secrets stored** — environment variables for child servers are passed at install time, not persisted.
129
+
130
+ ## Development
131
+
132
+ ```bash
133
+ git clone https://github.com/isaac-levine/forage.git
134
+ cd forage
135
+ npm install
136
+ npm run build
137
+ ```
138
+
139
+ Test locally with Claude Code:
140
+
141
+ ```bash
142
+ claude mcp add forage-dev -- node /path/to/forage/dist/server.js
143
+ ```
144
+
145
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
146
+
147
+ ## Roadmap
148
+
149
+ - [ ] `forage update` — check for newer versions of installed tools
150
+ - [ ] Support for pip/cargo/brew packages (not just npm)
151
+ - [ ] Smarter search ranking (weight by downloads, stars, description relevance)
152
+ - [ ] `server.json` submission to the Official MCP Registry
153
+ - [ ] Landing page at forage.dev
154
+
155
+ ## License
156
+
157
+ [MIT](LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
3
+ import { exec } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { forage_search } from "./tools/search.js";
6
+ import { listTools } from "./persistence/manifest.js";
7
+ const execAsync = promisify(exec);
8
+ const HELP = `
9
+ forage — Self-improving tool discovery for AI agents
10
+
11
+ Usage:
12
+ forage init [--client <name>] Set up Forage for your AI agent
13
+ forage search <query> Search for MCP servers
14
+ forage list List installed tools
15
+ forage help Show this help message
16
+
17
+ Examples:
18
+ forage init Add Forage to Claude Code
19
+ forage init --client cursor Add Forage to Cursor
20
+ forage search "postgres database"
21
+ forage list
22
+ `.trim();
23
+ async function main() {
24
+ const args = process.argv.slice(2);
25
+ if (args.length === 0 || args[0] === "help" || args[0] === "--help") {
26
+ console.log(HELP);
27
+ return;
28
+ }
29
+ const command = args[0];
30
+ switch (command) {
31
+ case "init":
32
+ await handleInit(args.slice(1));
33
+ break;
34
+ case "search":
35
+ await handleSearch(args.slice(1));
36
+ break;
37
+ case "list":
38
+ await handleList();
39
+ break;
40
+ default:
41
+ console.error(`Unknown command: ${command}`);
42
+ console.log(HELP);
43
+ process.exit(1);
44
+ }
45
+ }
46
+ async function handleInit(args) {
47
+ const { values } = parseArgs({
48
+ args,
49
+ options: {
50
+ client: { type: "string", default: "claude-code" },
51
+ },
52
+ allowPositionals: false,
53
+ });
54
+ const client = values.client ?? "claude-code";
55
+ switch (client) {
56
+ case "claude-code": {
57
+ console.log("Adding Forage to Claude Code...");
58
+ try {
59
+ await execAsync("claude mcp add forage -- npx -y forage-mcp");
60
+ console.log("Done! Forage has been added to Claude Code.");
61
+ console.log("Start a new Claude Code session to use it.");
62
+ }
63
+ catch {
64
+ console.log("Could not run `claude mcp add` automatically.");
65
+ console.log("Run this command manually:");
66
+ console.log("");
67
+ console.log(" claude mcp add forage -- npx -y forage-mcp");
68
+ }
69
+ break;
70
+ }
71
+ case "cursor": {
72
+ console.log("Adding Forage to Cursor...");
73
+ const fs = await import("node:fs/promises");
74
+ const path = await import("node:path");
75
+ const mcpConfigPath = path.join(process.cwd(), ".cursor", "mcp.json");
76
+ let config = {};
77
+ try {
78
+ const existing = await fs.readFile(mcpConfigPath, "utf-8");
79
+ config = JSON.parse(existing);
80
+ }
81
+ catch {
82
+ // No existing config
83
+ }
84
+ const mcpServers = config.mcpServers ?? {};
85
+ mcpServers.forage = {
86
+ command: "npx",
87
+ args: ["-y", "forage-mcp"],
88
+ };
89
+ config.mcpServers = mcpServers;
90
+ await fs.mkdir(path.dirname(mcpConfigPath), { recursive: true });
91
+ await fs.writeFile(mcpConfigPath, JSON.stringify(config, null, 2), "utf-8");
92
+ console.log(`Written to ${mcpConfigPath}`);
93
+ console.log("Restart Cursor to use Forage.");
94
+ break;
95
+ }
96
+ default:
97
+ console.error(`Unsupported client: ${client}`);
98
+ console.log("Supported clients: claude-code, cursor");
99
+ process.exit(1);
100
+ }
101
+ }
102
+ async function handleSearch(args) {
103
+ const query = args.join(" ");
104
+ if (!query) {
105
+ console.error("Usage: forage search <query>");
106
+ process.exit(1);
107
+ }
108
+ console.log(`Searching for "${query}"...\n`);
109
+ const { results } = await forage_search({ query });
110
+ if (results.length === 0) {
111
+ console.log("No results found.");
112
+ return;
113
+ }
114
+ for (const result of results) {
115
+ const badge = result.source === "official-registry"
116
+ ? "[official]"
117
+ : result.source === "smithery"
118
+ ? "[smithery]"
119
+ : "[npm]";
120
+ console.log(` ${badge} ${result.packageName}`);
121
+ console.log(` ${result.description}`);
122
+ if (result.url)
123
+ console.log(` ${result.url}`);
124
+ console.log("");
125
+ }
126
+ }
127
+ async function handleList() {
128
+ const tools = await listTools();
129
+ if (tools.length === 0) {
130
+ console.log("No tools installed via Forage.");
131
+ return;
132
+ }
133
+ console.log("Installed tools:\n");
134
+ for (const tool of tools) {
135
+ console.log(` ${tool.name}`);
136
+ console.log(` Package: ${tool.packageName}`);
137
+ console.log(` Installed: ${tool.installedAt}`);
138
+ console.log(` Auto-start: ${tool.autoStart}`);
139
+ console.log("");
140
+ }
141
+ }
142
+ main().catch((error) => {
143
+ console.error(error);
144
+ process.exit(1);
145
+ });
146
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface LogEntry {
2
+ timestamp: string;
3
+ action: "install" | "uninstall";
4
+ packageName: string;
5
+ version?: string;
6
+ source?: string;
7
+ success: boolean;
8
+ error?: string;
9
+ }
10
+ export declare function appendLog(entry: LogEntry): Promise<void>;
11
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/persistence/log.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD,wBAAsB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9D"}
@@ -0,0 +1,21 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import { ensureForageDir } from "./manifest.js";
5
+ const LOG_PATH = path.join(os.homedir(), ".forage", "install-log.json");
6
+ async function readLog() {
7
+ try {
8
+ const data = await fs.readFile(LOG_PATH, "utf-8");
9
+ return JSON.parse(data);
10
+ }
11
+ catch {
12
+ return [];
13
+ }
14
+ }
15
+ export async function appendLog(entry) {
16
+ await ensureForageDir();
17
+ const log = await readLog();
18
+ log.push(entry);
19
+ await fs.writeFile(LOG_PATH, JSON.stringify(log, null, 2), "utf-8");
20
+ }
21
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/persistence/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAYhD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;AAExE,KAAK,UAAU,OAAO;IACpB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAe;IAC7C,MAAM,eAAe,EAAE,CAAC;IACxB,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;IAC5B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface InstalledTool {
2
+ name: string;
3
+ packageName: string;
4
+ version: string;
5
+ source: "npm" | "official-registry" | "smithery";
6
+ command: string;
7
+ args: string[];
8
+ env?: Record<string, string>;
9
+ autoStart: boolean;
10
+ installedAt: string;
11
+ rules?: string;
12
+ }
13
+ export interface Manifest {
14
+ version: 1;
15
+ tools: Record<string, InstalledTool>;
16
+ }
17
+ export declare function ensureForageDir(): Promise<void>;
18
+ export declare function readManifest(): Promise<Manifest>;
19
+ export declare function writeManifest(manifest: Manifest): Promise<void>;
20
+ export declare function addTool(tool: InstalledTool): Promise<void>;
21
+ export declare function removeTool(name: string): Promise<InstalledTool | null>;
22
+ export declare function getTool(name: string): Promise<InstalledTool | null>;
23
+ export declare function listTools(): Promise<InstalledTool[]>;
24
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/persistence/manifest.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,KAAK,GAAG,mBAAmB,GAAG,UAAU,CAAC;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACtC;AAKD,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrD;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,CAOtD;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrE;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAQ5E;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAGzE;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAG1D"}
@@ -0,0 +1,45 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ const FORAGE_DIR = path.join(os.homedir(), ".forage");
5
+ const MANIFEST_PATH = path.join(FORAGE_DIR, "manifest.json");
6
+ export async function ensureForageDir() {
7
+ await fs.mkdir(FORAGE_DIR, { recursive: true });
8
+ await fs.mkdir(path.join(FORAGE_DIR, "cache"), { recursive: true });
9
+ }
10
+ export async function readManifest() {
11
+ try {
12
+ const data = await fs.readFile(MANIFEST_PATH, "utf-8");
13
+ return JSON.parse(data);
14
+ }
15
+ catch {
16
+ return { version: 1, tools: {} };
17
+ }
18
+ }
19
+ export async function writeManifest(manifest) {
20
+ await ensureForageDir();
21
+ await fs.writeFile(MANIFEST_PATH, JSON.stringify(manifest, null, 2), "utf-8");
22
+ }
23
+ export async function addTool(tool) {
24
+ const manifest = await readManifest();
25
+ manifest.tools[tool.name] = tool;
26
+ await writeManifest(manifest);
27
+ }
28
+ export async function removeTool(name) {
29
+ const manifest = await readManifest();
30
+ const tool = manifest.tools[name] ?? null;
31
+ if (tool) {
32
+ delete manifest.tools[name];
33
+ await writeManifest(manifest);
34
+ }
35
+ return tool;
36
+ }
37
+ export async function getTool(name) {
38
+ const manifest = await readManifest();
39
+ return manifest.tools[name] ?? null;
40
+ }
41
+ export async function listTools() {
42
+ const manifest = await readManifest();
43
+ return Object.values(manifest.tools);
44
+ }
45
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/persistence/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAoBzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AACtD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAa,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAkB;IACpD,MAAM,eAAe,EAAE,CAAC;IACxB,MAAM,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAmB;IAC/C,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjC,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAC1C,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAY;IACxC,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import type { InstalledTool } from "../persistence/manifest.js";
4
+ export interface ManagedServer {
5
+ name: string;
6
+ client: Client;
7
+ transport: StdioClientTransport;
8
+ tools: Array<{
9
+ name: string;
10
+ description?: string;
11
+ inputSchema: Record<string, unknown>;
12
+ }>;
13
+ }
14
+ export declare function startServer(tool: InstalledTool): Promise<ManagedServer>;
15
+ export declare function stopServer(name: string): Promise<boolean>;
16
+ export declare function callTool(serverName: string, toolName: string, args: Record<string, unknown>): Promise<unknown>;
17
+ export declare function getRunningServers(): Map<string, ManagedServer>;
18
+ export declare function getServer(name: string): ManagedServer | undefined;
19
+ export declare function stopAll(): Promise<void>;
20
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/proxy/manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,oBAAoB,CAAC;IAChC,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,CAAC,CAAC;CACJ;AAID,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAuC7E;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAY/D;AAED,wBAAsB,QAAQ,CAC5B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAYlB;AAED,wBAAgB,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAE9D;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAEjE;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7C"}
@@ -0,0 +1,77 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ const servers = new Map();
4
+ export async function startServer(tool) {
5
+ // If already running, return existing
6
+ const existing = servers.get(tool.name);
7
+ if (existing) {
8
+ return existing;
9
+ }
10
+ const transport = new StdioClientTransport({
11
+ command: tool.command,
12
+ args: tool.args,
13
+ env: tool.env
14
+ ? { ...filterEnv(process.env), ...tool.env }
15
+ : filterEnv(process.env),
16
+ });
17
+ const client = new Client({ name: `forage-proxy-${tool.name}`, version: "0.1.0" }, { capabilities: {} });
18
+ await client.connect(transport);
19
+ // Discover the child server's tools
20
+ const toolsList = await client.listTools();
21
+ const tools = (toolsList.tools ?? []).map((t) => ({
22
+ name: t.name,
23
+ description: t.description,
24
+ inputSchema: t.inputSchema,
25
+ }));
26
+ const managed = {
27
+ name: tool.name,
28
+ client,
29
+ transport,
30
+ tools,
31
+ };
32
+ servers.set(tool.name, managed);
33
+ return managed;
34
+ }
35
+ export async function stopServer(name) {
36
+ const server = servers.get(name);
37
+ if (!server)
38
+ return false;
39
+ try {
40
+ await server.client.close();
41
+ }
42
+ catch {
43
+ // Ignore close errors — transport cleanup handles it
44
+ }
45
+ servers.delete(name);
46
+ return true;
47
+ }
48
+ export async function callTool(serverName, toolName, args) {
49
+ const server = servers.get(serverName);
50
+ if (!server) {
51
+ throw new Error(`Server "${serverName}" is not running`);
52
+ }
53
+ const result = await server.client.callTool({
54
+ name: toolName,
55
+ arguments: args,
56
+ });
57
+ return result;
58
+ }
59
+ export function getRunningServers() {
60
+ return servers;
61
+ }
62
+ export function getServer(name) {
63
+ return servers.get(name);
64
+ }
65
+ export async function stopAll() {
66
+ const names = [...servers.keys()];
67
+ await Promise.all(names.map((name) => stopServer(name)));
68
+ }
69
+ function filterEnv(env) {
70
+ const result = {};
71
+ for (const [key, value] of Object.entries(env)) {
72
+ if (value !== undefined)
73
+ result[key] = value;
74
+ }
75
+ return result;
76
+ }
77
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/proxy/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface WrappedTool {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: Record<string, unknown>;
5
+ serverName: string;
6
+ originalName: string;
7
+ }
8
+ /**
9
+ * Get all tools from all running proxied servers, namespaced to avoid collisions.
10
+ * Tool names are prefixed with the server name: "server-postgres__query" -> "foraged__server-postgres__query"
11
+ */
12
+ export declare function getWrappedTools(): WrappedTool[];
13
+ /**
14
+ * Parse a wrapped tool name back to server name + original tool name.
15
+ */
16
+ export declare function parseWrappedToolName(name: string): {
17
+ serverName: string;
18
+ toolName: string;
19
+ } | null;
20
+ //# sourceMappingURL=wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../src/proxy/wrapper.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,WAAW,EAAE,CAgB/C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,GACX;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAOjD"}
@@ -0,0 +1,37 @@
1
+ import { getRunningServers } from "./manager.js";
2
+ /**
3
+ * Get all tools from all running proxied servers, namespaced to avoid collisions.
4
+ * Tool names are prefixed with the server name: "server-postgres__query" -> "foraged__server-postgres__query"
5
+ */
6
+ export function getWrappedTools() {
7
+ const tools = [];
8
+ for (const [serverName, server] of getRunningServers()) {
9
+ for (const tool of server.tools) {
10
+ tools.push({
11
+ name: `foraged__${sanitizeName(serverName)}__${tool.name}`,
12
+ description: `[via ${serverName}] ${tool.description ?? ""}`.trim(),
13
+ inputSchema: tool.inputSchema,
14
+ serverName,
15
+ originalName: tool.name,
16
+ });
17
+ }
18
+ }
19
+ return tools;
20
+ }
21
+ /**
22
+ * Parse a wrapped tool name back to server name + original tool name.
23
+ */
24
+ export function parseWrappedToolName(name) {
25
+ if (!name.startsWith("foraged__"))
26
+ return null;
27
+ const parts = name.slice("foraged__".length).split("__");
28
+ if (parts.length < 2)
29
+ return null;
30
+ const serverName = parts[0];
31
+ const toolName = parts.slice(1).join("__");
32
+ return { serverName, toolName };
33
+ }
34
+ function sanitizeName(name) {
35
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_");
36
+ }
37
+ //# sourceMappingURL=wrapper.js.map