@onedotmint/siftpath 0.0.0-stage → 2.0.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.
- package/LICENSE +21 -0
- package/README.md +165 -2
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +144 -0
- package/dist/client.js +130 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +107 -0
- package/dist/config.js +127 -0
- package/dist/config.js.map +1 -0
- package/dist/defaults.d.ts +38 -0
- package/dist/defaults.js +31 -0
- package/dist/defaults.js.map +1 -0
- package/dist/diagnostics.d.ts +158 -0
- package/dist/diagnostics.js +202 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/inputs.d.ts +25 -0
- package/dist/inputs.js +27 -0
- package/dist/inputs.js.map +1 -0
- package/dist/mcp/stdio.d.ts +2 -0
- package/dist/mcp/stdio.js +43 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/output.d.ts +23 -0
- package/dist/output.js +118 -0
- package/dist/output.js.map +1 -0
- package/dist/router.d.ts +20 -0
- package/dist/router.js +85 -0
- package/dist/router.js.map +1 -0
- package/dist/setup-ui.d.ts +8 -0
- package/dist/setup-ui.js +3032 -0
- package/dist/setup-ui.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +50 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools.d.ts +35 -0
- package/dist/tools.js +17 -0
- package/dist/tools.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/docs/ARCHITECTURE.md +33 -0
- package/docs/CONFIGURATION.md +127 -0
- package/docs/DIAGNOSTICS.md +109 -0
- package/docs/MIGRATION.md +13 -0
- package/docs/PROVIDERS.md +43 -0
- package/docs/SECURITY.md +15 -0
- package/docs/TESTING.md +138 -0
- package/examples/anonymous.json +36 -0
- package/extensions/siftpath.mjs +32 -0
- package/package.json +60 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 GuDaStudio
|
|
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
CHANGED
|
@@ -1,3 +1,166 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Siftpath
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Siftpath is a small TypeScript/Node.js 22+ MCP router for AI agents. It exposes exactly two model-facing tools:
|
|
4
|
+
|
|
5
|
+
- `web_search({ query, intent?, maxResults? })`
|
|
6
|
+
- `web_read({ url, focus? })`
|
|
7
|
+
|
|
8
|
+
It does not search, parse HTML, chunk, rerank, merge, or run an LLM. It lazily connects to configured official MCP servers, maps the two stable inputs to their configured tool names, forwards the official result, keeps source URLs, bounds the serialized output, and tries configured fallbacks sequentially.
|
|
9
|
+
|
|
10
|
+
## Install and run
|
|
11
|
+
|
|
12
|
+
Install the stable release (Node.js 22+; npm `latest` tag):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @onedotmint/siftpath@latest
|
|
16
|
+
siftpath --version
|
|
17
|
+
siftpath setup
|
|
18
|
+
siftpath status
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Alternatively, build from an existing source checkout:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm ci
|
|
25
|
+
npm run build
|
|
26
|
+
# Optional: npm link (installs the siftpath command for this source checkout)
|
|
27
|
+
node dist/cli.js mcp
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
In an MCP client, the simplest local configuration is:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"siftpath": {
|
|
36
|
+
"command": "node",
|
|
37
|
+
"args": ["/absolute/path/to/Siftpath/dist/cli.js", "mcp"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The same process serves stdio MCP. It requires no Docker, database, browser, background daemon, local model, or HTTP server.
|
|
44
|
+
|
|
45
|
+
With the global npm install or `npm link` (otherwise replace `siftpath` with `node dist/cli.js`):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
siftpath doctor
|
|
49
|
+
siftpath search "Node.js 22 release notes" --intent general
|
|
50
|
+
siftpath search "semantic similarity API" --intent semantic --max-results 5
|
|
51
|
+
siftpath read https://example.com --focus "What is this page about?"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To install the single package elsewhere, run `npm pack` here, then `npm install /absolute/path/to/onedotmint-siftpath-2.0.0.tgz` in the consuming project (or `npm install -g` for the CLI). The tarball includes compiled runtime, declarations, docs and the anonymous config example.
|
|
55
|
+
|
|
56
|
+
SDK example:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { Siftpath, readConfiguration } from '@onedotmint/siftpath';
|
|
60
|
+
const siftpath = new Siftpath(await readConfiguration());
|
|
61
|
+
try {
|
|
62
|
+
const result = await siftpath.search({ query: 'Node.js documentation' });
|
|
63
|
+
console.log(result.content, result.sources);
|
|
64
|
+
} finally { await siftpath.close(); }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Try without provider keys
|
|
68
|
+
|
|
69
|
+
Exa and Jina currently offer anonymous hosted MCP access. To opt in (quotas/availability may change):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
SIFTPATH_CONFIG="$PWD/examples/anonymous.json" node dist/cli.js search "Node.js documentation"
|
|
73
|
+
SIFTPATH_CONFIG="$PWD/examples/anonymous.json" node dist/cli.js read https://example.com
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For a local MCP client, add `"env": { "SIFTPATH_CONFIG": "/absolute/path/to/Siftpath/examples/anonymous.json" }` to the server entry above. Brave remains the general primary when a key exists, otherwise it falls back to Exa. These examples write no private configuration or secrets. See [配置与可选服务](docs/CONFIGURATION.md), [接口核验](docs/PROVIDERS.md), [验证记录](docs/TESTING.md), [迁移](docs/MIGRATION.md), and [安全边界](docs/SECURITY.md).
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
Run `siftpath setup` for the Clack wizard: arrows navigate, Space selects providers, Enter confirms, Esc/Ctrl+C cancels without saving. Select Brave/Exa/Tavily/Jina/Firecrawl, keep or replace hidden Keys (or explicitly choose supported anonymous access), choose default search/read and ordered fallback, then optionally confirm real tests. No tool-name/JSON editing is required.
|
|
81
|
+
|
|
82
|
+
`setup --non-interactive` only creates minimal defaults at `~/.config/siftpath/config.json` (or `SIFTPATH_CONFIG`). Keys can also be supplied as environment variables. Configuration is private JSON, written atomically with mode 0600; secrets are never printed or put into model schemas.
|
|
83
|
+
|
|
84
|
+
The built-in routes are:
|
|
85
|
+
|
|
86
|
+
- general/default search → Brave `brave_web_search`
|
|
87
|
+
- semantic search → Exa `web_search_exa`
|
|
88
|
+
- research → optional configured provider (Tavily is an example)
|
|
89
|
+
- read → Jina `read_url`, with `focus` mapped to its native `question` parameter
|
|
90
|
+
|
|
91
|
+
Only a provider with a complete connection and all required credential references resolved is attempted. The default config uses:
|
|
92
|
+
|
|
93
|
+
- Brave stdio: `npx -y @brave/brave-search-mcp-server@2.1.4 --transport stdio`, `BRAVE_API_KEY`
|
|
94
|
+
- Exa stdio: `npx -y exa-mcp-server@3.4.2`, `EXA_API_KEY`
|
|
95
|
+
- Jina Streamable HTTP: `https://mcp.jina.ai/v1`, `JINA_API_KEY` for the default authenticated route; anonymous reading is an explicit opt-in
|
|
96
|
+
|
|
97
|
+
The versions and tool names above were checked against the official repositories on this rebuild. Check the official servers before upgrading: hosted/runtime schemas can change independently of their source README. Jina `question` is native; if a configured reader has no `$focus` mapping, Siftpath returns `FOCUS_UNSUPPORTED` rather than pretending to extract locally.
|
|
98
|
+
|
|
99
|
+
Minimal custom provider example:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"version": 2,
|
|
104
|
+
"providers": {
|
|
105
|
+
"brave": {
|
|
106
|
+
"enabled": true,
|
|
107
|
+
"connection": {
|
|
108
|
+
"transport": "stdio",
|
|
109
|
+
"command": "npx",
|
|
110
|
+
"args": ["-y", "@brave/brave-search-mcp-server@2.1.4", "--transport", "stdio"],
|
|
111
|
+
"env": { "BRAVE_API_KEY": "${BRAVE_API_KEY}" }
|
|
112
|
+
},
|
|
113
|
+
"search": {
|
|
114
|
+
"name": "brave_web_search",
|
|
115
|
+
"arguments": { "query": "$query", "count": "$maxResults" }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"routes": { "search": "brave", "read": "jina", "intents": {}, "searchFallback": [], "readFallback": [] }
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Connection transports are `stdio` (`command`, `args`, `env`) and Streamable HTTP (`url`, `headers`). HTTP URLs reject credentials, query-string secrets, and insecure non-loopback HTTP. Use `${ENV_NAME}` in connection env/headers, never in commands, arguments or URLs; environment variables override saved credentials. `doctor` reports `READY`, `MISSING_CREDENTIALS`, `DISABLED`, and unsupported route diagnostics without making network calls.
|
|
124
|
+
|
|
125
|
+
Tavily and Firecrawl have built-in presets, disabled until selected in `setup`. Tavily uses official `tavily-mcp@0.2.22` / `tavily_search` (basic, 5–20 results), with a Key or its native stdio Keyless mode. Firecrawl uses official `firecrawl-mcp@3.33.4` / `firecrawl_scrape` with a Key, or the official anonymous hosted MCP. Both were verified through real MCP calls. See [配置向导](docs/CONFIGURATION.md). Clack 1.8.1 and esbuild 0.25.12 are build-only: the ~111 KiB setup bundle is dynamically loaded only in interactive setup, with no new runtime dependency.
|
|
126
|
+
|
|
127
|
+
## Output and safety
|
|
128
|
+
|
|
129
|
+
Each successful result includes the selected provider, official tool name, retrieval timestamp, original URL where relevant, traceable public source URLs, `UNTRUSTED_WEB_CONTENT`, fallback diagnostics, and `truncated`/`sourcesTruncated` flags. The returned `content` is official output, not a Siftpath-generated answer. Treat it as data, never instructions.
|
|
130
|
+
|
|
131
|
+
Public HTTP(S) URLs only are accepted for reads. IP literals, local names, credentials and reverse-DNS names are rejected. Provider failures are sanitized into stable diagnostics; raw upstream bodies, API keys and URLs containing credentials are not returned. Connections are initialized on first use, reused within one process, and closed on normal MCP shutdown.
|
|
132
|
+
|
|
133
|
+
## Status and Pi command
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
siftpath status # offline, never treats READY as tested availability
|
|
137
|
+
siftpath status --json
|
|
138
|
+
siftpath test brave --json # real request to this provider only; no fallback
|
|
139
|
+
siftpath test all # sequential checks, may use API quota
|
|
140
|
+
|
|
141
|
+
# Install the Pi command extension from the stable npm release:
|
|
142
|
+
pi install npm:@onedotmint/siftpath@latest
|
|
143
|
+
|
|
144
|
+
# Source-checkout alternatives (choose one instead of the npm install):
|
|
145
|
+
# After building, try without changing Pi settings:
|
|
146
|
+
pi -e /absolute/path/to/Siftpath/extensions/siftpath.mjs
|
|
147
|
+
# Or install persistently: pi install /absolute/path/to/Siftpath
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
After installation, restart Pi or run `/reload`. In Pi: `/siftpath`, `/siftpath test brave`, `/siftpath test all`. The extension uses the same private configuration and credential environment as the CLI; set `SIFTPATH_CONFIG` before starting Pi to select another file. The extension only invokes the packaged CLI and displays its summary using native notifications. No model tools are added. Connection, tool discovery and real-request stages are reported separately; recent results are stored privately alongside the selected config, with changed configurations marked stale. See [诊断与 Pi 安装/真实验收](docs/DIAGNOSTICS.md).
|
|
151
|
+
|
|
152
|
+
## Tests and verification
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npm run typecheck
|
|
156
|
+
npm test
|
|
157
|
+
npm run test:live # skips network/quota tests unless explicitly enabled
|
|
158
|
+
SIFTPATH_LIVE=1 npm run test:live # Exa anonymous + Jina anonymous in this rebuild
|
|
159
|
+
SIFTPATH_CONTRACT=1 npm run test:live # tools/list contract checks only
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Offline tests use real MCP SDK fixture processes and local HTTP MCP servers, not mocked vendor HTTP APIs: 81 tests cover tool exposure, stdio, Streamable HTTP, configuration, fallback, cancellation, timeout, output bounds, URL safety, provenance and shutdown. This repository run also verified real anonymous Exa search and Jina read, plus the full Siftpath stdio path. Brave real search was not verified because `BRAVE_API_KEY` was absent. Tavily anonymous official stdio search and Firecrawl anonymous hosted reading were also live-tested; account Key flows were not verified.
|
|
163
|
+
|
|
164
|
+
## Deliberate non-goals
|
|
165
|
+
|
|
166
|
+
The old EvidenceEngine, Gather path, multi-provider fusion, custom relevance scoring, sufficiency judgment, HTTP server, old Pi model-tool extension, generated host plugins, and research orchestration were removed from the active package. Siftpath is not a search engine or answer generator; official MCP services own those capabilities.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
The repository's MIT LICENSE is preserved from SmartSearch at `a6e310d8c51d1fb9eb454d27afbbc3f81cc2e61a`, including Copyright (c) 2025 GuDaStudio.
|
|
4
|
+
|
|
5
|
+
This rebuild retains and adapts the project's private JSON configuration storage, hidden credential entry, public URL guard, CLI conventions and package-version reader. The old evidence engine and historical evaluation fixtures are no longer distributed.
|
|
6
|
+
|
|
7
|
+
Runtime npm dependencies are @modelcontextprotocol/sdk (MIT) and Zod (MIT), with transitive dependencies installed from their registries; inspect package-lock.json and the installed packages' licenses. TypeScript (Apache-2.0), @types/node (MIT), undici-types (MIT) are development dependencies. Official provider MCP packages are launched separately on demand and retain their own licenses/terms.
|
|
8
|
+
|
|
9
|
+
Official provider source and web-search-plus-mcp/agntn-web were interface/design references, not vendored implementation. Precise links and inspected revisions: docs/PROVIDERS.md. No upstream search/reader/ranking implementation is copied into Siftpath.
|
|
10
|
+
|
|
11
|
+
## Bundled interactive setup
|
|
12
|
+
|
|
13
|
+
The lazily loaded `dist/setup-ui.js` bundles @clack/prompts 1.8.1, @clack/core 1.5.1, sisteransi 1.0.5, fast-wrap-ansi 0.2.2, fast-string-width 3.0.2 and fast-string-truncated-width 3.0.3 (all MIT). Clack and esbuild 0.25.12 are build-only dependencies, not runtime package requirements.
|
|
14
|
+
|
|
15
|
+
Copyright notices for bundled MIT code:
|
|
16
|
+
|
|
17
|
+
- Copyright (c) 2025-Present Bombshell contributors (https://bomb.sh/team) — Clack
|
|
18
|
+
- Copyright (c) 2018 Terkel Gjervig Nielsen — sisteransi
|
|
19
|
+
- Copyright (c) 2024-present Fabio Spampinato — fast-string-width / fast-string-truncated-width
|
|
20
|
+
- Copyright (c) 2025 James Garbutt; Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) — fast-wrap-ansi
|
|
21
|
+
|
|
22
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
25
|
+
|
|
26
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { configPath, readConfiguration, diagnostics } from './config.js';
|
|
6
|
+
import { statusReport, testReport, formatStatus, formatTests } from './diagnostics.js';
|
|
7
|
+
import { SiftpathError, safeError } from './errors.js';
|
|
8
|
+
import { Siftpath } from './router.js';
|
|
9
|
+
import { serveStdio } from './mcp/stdio.js';
|
|
10
|
+
import { setup } from './setup.js';
|
|
11
|
+
import { packageVersion } from './version.js';
|
|
12
|
+
import { invokeTool } from './tools.js';
|
|
13
|
+
const help = `Siftpath — lightweight official MCP router (Node.js 22+)
|
|
14
|
+
siftpath mcp
|
|
15
|
+
siftpath search "question" [--intent general|semantic|research] [--max-results 5]
|
|
16
|
+
siftpath read URL [--focus "question about the page"]
|
|
17
|
+
siftpath status [--json] # offline configuration and last tests
|
|
18
|
+
siftpath test <provider|all> [--json] # real requests, no fallback; may use API quota
|
|
19
|
+
siftpath doctor
|
|
20
|
+
siftpath setup [--non-interactive] # Clack provider/Key/routes wizard; tests are opt-in
|
|
21
|
+
siftpath --version
|
|
22
|
+
Configuration: SIFTPATH_CONFIG or ~/.config/siftpath/config.json (JSON).
|
|
23
|
+
Keys: BRAVE_API_KEY, EXA_API_KEY, JINA_API_KEY; optional TAVILY_API_KEY, FIRECRAWL_API_KEY.
|
|
24
|
+
Only the selected provider is called; failures try configured fallbacks sequentially.
|
|
25
|
+
`;
|
|
26
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = parseArgs({ args: argv, allowPositionals: true, strict: true, options: {
|
|
30
|
+
help: { type: 'boolean', short: 'h' }, version: { type: 'boolean', short: 'v' }, intent: { type: 'string' },
|
|
31
|
+
json: { type: 'boolean' }, 'max-results': { type: 'string' }, focus: { type: 'string' }, 'non-interactive': { type: 'boolean' },
|
|
32
|
+
} });
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new SiftpathError('INVALID_INPUT', 'Invalid command-line arguments. Run siftpath --help.');
|
|
36
|
+
}
|
|
37
|
+
const { values, positionals: [command, ...args] } = parsed;
|
|
38
|
+
if (command === 'search' && values.focus !== undefined || command === 'read' && (values.intent !== undefined || values['max-results'] !== undefined))
|
|
39
|
+
throw new SiftpathError('INVALID_INPUT', 'Options do not match the selected command. Run siftpath --help.');
|
|
40
|
+
if (values.help || !command && !values.version) {
|
|
41
|
+
process.stdout.write(help);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (values.version) {
|
|
45
|
+
process.stdout.write(packageVersion + '\n');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (command === 'setup') {
|
|
49
|
+
await setup(Boolean(values['non-interactive']));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!['mcp', 'search', 'read', 'doctor', 'status', 'test'].includes(command))
|
|
53
|
+
throw new SiftpathError('INVALID_INPUT', 'Unknown command. Run siftpath --help.');
|
|
54
|
+
const config = await readConfiguration();
|
|
55
|
+
if (command === 'doctor') {
|
|
56
|
+
process.stdout.write(JSON.stringify(diagnostics(config), null, 2) + '\n');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (command === 'status') {
|
|
60
|
+
if (args.length)
|
|
61
|
+
throw new SiftpathError('INVALID_INPUT', 'status takes no positional arguments.');
|
|
62
|
+
const report = await statusReport(config);
|
|
63
|
+
process.stdout.write((values.json ? JSON.stringify({ ...report, summary: formatStatus(report) }) : formatStatus(report)) + '\n');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (command === 'test') {
|
|
67
|
+
if (args.length !== 1)
|
|
68
|
+
throw new SiftpathError('INVALID_INPUT', 'Use siftpath test <provider|all> [--json].');
|
|
69
|
+
const controller = new AbortController(), stop = () => controller.abort();
|
|
70
|
+
process.once('SIGINT', stop);
|
|
71
|
+
process.once('SIGTERM', stop);
|
|
72
|
+
try {
|
|
73
|
+
const report = await testReport(config, args[0], configPath(), process.env, controller.signal);
|
|
74
|
+
process.stdout.write((values.json ? JSON.stringify({ ...report, summary: formatTests(report) }) : formatTests(report)) + '\n');
|
|
75
|
+
if (!report.ok)
|
|
76
|
+
process.exitCode = controller.signal.aborted ? 130 : 1;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
process.off('SIGINT', stop);
|
|
80
|
+
process.off('SIGTERM', stop);
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const router = new Siftpath(config);
|
|
85
|
+
if (command === 'mcp') {
|
|
86
|
+
try {
|
|
87
|
+
await serveStdio(router);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
await router.close();
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const controller = new AbortController(), stop = () => controller.abort();
|
|
95
|
+
process.once('SIGINT', stop);
|
|
96
|
+
process.once('SIGTERM', stop);
|
|
97
|
+
try {
|
|
98
|
+
if (command === 'read' && args.length !== 1)
|
|
99
|
+
throw new SiftpathError('INVALID_INPUT', 'read requires exactly one URL.');
|
|
100
|
+
const input = command === 'search'
|
|
101
|
+
? { query: args.join(' '), ...(values.intent !== undefined ? { intent: values.intent } : {}),
|
|
102
|
+
...(values['max-results'] !== undefined ? { maxResults: Number(values['max-results']) } : {}) }
|
|
103
|
+
: { url: args[0], ...(values.focus !== undefined ? { focus: values.focus } : {}) };
|
|
104
|
+
const result = await invokeTool(router, `web_${command}`, input, { signal: controller.signal });
|
|
105
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
process.off('SIGINT', stop);
|
|
109
|
+
process.off('SIGTERM', stop);
|
|
110
|
+
await router.close();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
114
|
+
main().catch(error => { const safe = safeError(error); process.stderr.write(JSON.stringify(safe) + '\n'); process.exitCode = safe.code === 'ABORTED' ? 130 : 1; });
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { type Configuration } from './config.js';
|
|
2
|
+
export declare function abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T>;
|
|
3
|
+
/** One lazy connection per configured MCP, shared by all router calls in this process. */
|
|
4
|
+
export declare class McpClients {
|
|
5
|
+
private config;
|
|
6
|
+
private env;
|
|
7
|
+
private connections;
|
|
8
|
+
private closed;
|
|
9
|
+
private closing?;
|
|
10
|
+
private requestSignal;
|
|
11
|
+
private fetch;
|
|
12
|
+
constructor(config: Configuration, env?: NodeJS.ProcessEnv);
|
|
13
|
+
private get;
|
|
14
|
+
connect(id: string, signal: AbortSignal): Promise<void>;
|
|
15
|
+
listTools(id: string, signal: AbortSignal, cursor?: string): Promise<{
|
|
16
|
+
[x: string]: unknown;
|
|
17
|
+
tools: {
|
|
18
|
+
inputSchema: {
|
|
19
|
+
[x: string]: unknown;
|
|
20
|
+
type: "object";
|
|
21
|
+
properties?: Record<string, object> | undefined;
|
|
22
|
+
required?: string[] | undefined;
|
|
23
|
+
};
|
|
24
|
+
name: string;
|
|
25
|
+
description?: string | undefined;
|
|
26
|
+
outputSchema?: {
|
|
27
|
+
[x: string]: unknown;
|
|
28
|
+
type: "object";
|
|
29
|
+
properties?: Record<string, object> | undefined;
|
|
30
|
+
required?: string[] | undefined;
|
|
31
|
+
} | undefined;
|
|
32
|
+
annotations?: {
|
|
33
|
+
title?: string | undefined;
|
|
34
|
+
readOnlyHint?: boolean | undefined;
|
|
35
|
+
destructiveHint?: boolean | undefined;
|
|
36
|
+
idempotentHint?: boolean | undefined;
|
|
37
|
+
openWorldHint?: boolean | undefined;
|
|
38
|
+
} | undefined;
|
|
39
|
+
execution?: {
|
|
40
|
+
taskSupport?: "optional" | "required" | "forbidden" | undefined;
|
|
41
|
+
} | undefined;
|
|
42
|
+
_meta?: Record<string, unknown> | undefined;
|
|
43
|
+
icons?: {
|
|
44
|
+
src: string;
|
|
45
|
+
mimeType?: string | undefined;
|
|
46
|
+
sizes?: string[] | undefined;
|
|
47
|
+
theme?: "light" | "dark" | undefined;
|
|
48
|
+
}[] | undefined;
|
|
49
|
+
title?: string | undefined;
|
|
50
|
+
}[];
|
|
51
|
+
_meta?: {
|
|
52
|
+
[x: string]: unknown;
|
|
53
|
+
progressToken?: string | number | undefined;
|
|
54
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
55
|
+
taskId: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
} | undefined;
|
|
58
|
+
nextCursor?: string | undefined;
|
|
59
|
+
}>;
|
|
60
|
+
call(id: string, name: string, args: Record<string, unknown>, signal: AbortSignal): Promise<{
|
|
61
|
+
[x: string]: unknown;
|
|
62
|
+
content: ({
|
|
63
|
+
type: "text";
|
|
64
|
+
text: string;
|
|
65
|
+
annotations?: {
|
|
66
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
67
|
+
priority?: number | undefined;
|
|
68
|
+
lastModified?: string | undefined;
|
|
69
|
+
} | undefined;
|
|
70
|
+
_meta?: Record<string, unknown> | undefined;
|
|
71
|
+
} | {
|
|
72
|
+
type: "image";
|
|
73
|
+
data: string;
|
|
74
|
+
mimeType: string;
|
|
75
|
+
annotations?: {
|
|
76
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
77
|
+
priority?: number | undefined;
|
|
78
|
+
lastModified?: string | undefined;
|
|
79
|
+
} | undefined;
|
|
80
|
+
_meta?: Record<string, unknown> | undefined;
|
|
81
|
+
} | {
|
|
82
|
+
type: "audio";
|
|
83
|
+
data: string;
|
|
84
|
+
mimeType: string;
|
|
85
|
+
annotations?: {
|
|
86
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
87
|
+
priority?: number | undefined;
|
|
88
|
+
lastModified?: string | undefined;
|
|
89
|
+
} | undefined;
|
|
90
|
+
_meta?: Record<string, unknown> | undefined;
|
|
91
|
+
} | {
|
|
92
|
+
uri: string;
|
|
93
|
+
name: string;
|
|
94
|
+
type: "resource_link";
|
|
95
|
+
description?: string | undefined;
|
|
96
|
+
mimeType?: string | undefined;
|
|
97
|
+
size?: number | undefined;
|
|
98
|
+
annotations?: {
|
|
99
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
100
|
+
priority?: number | undefined;
|
|
101
|
+
lastModified?: string | undefined;
|
|
102
|
+
} | undefined;
|
|
103
|
+
_meta?: {
|
|
104
|
+
[x: string]: unknown;
|
|
105
|
+
} | undefined;
|
|
106
|
+
icons?: {
|
|
107
|
+
src: string;
|
|
108
|
+
mimeType?: string | undefined;
|
|
109
|
+
sizes?: string[] | undefined;
|
|
110
|
+
theme?: "light" | "dark" | undefined;
|
|
111
|
+
}[] | undefined;
|
|
112
|
+
title?: string | undefined;
|
|
113
|
+
} | {
|
|
114
|
+
type: "resource";
|
|
115
|
+
resource: {
|
|
116
|
+
uri: string;
|
|
117
|
+
text: string;
|
|
118
|
+
mimeType?: string | undefined;
|
|
119
|
+
_meta?: Record<string, unknown> | undefined;
|
|
120
|
+
} | {
|
|
121
|
+
uri: string;
|
|
122
|
+
blob: string;
|
|
123
|
+
mimeType?: string | undefined;
|
|
124
|
+
_meta?: Record<string, unknown> | undefined;
|
|
125
|
+
};
|
|
126
|
+
annotations?: {
|
|
127
|
+
audience?: ("user" | "assistant")[] | undefined;
|
|
128
|
+
priority?: number | undefined;
|
|
129
|
+
lastModified?: string | undefined;
|
|
130
|
+
} | undefined;
|
|
131
|
+
_meta?: Record<string, unknown> | undefined;
|
|
132
|
+
})[];
|
|
133
|
+
_meta?: {
|
|
134
|
+
[x: string]: unknown;
|
|
135
|
+
progressToken?: string | number | undefined;
|
|
136
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
137
|
+
taskId: string;
|
|
138
|
+
} | undefined;
|
|
139
|
+
} | undefined;
|
|
140
|
+
structuredContent?: Record<string, unknown> | undefined;
|
|
141
|
+
isError?: boolean | undefined;
|
|
142
|
+
}>;
|
|
143
|
+
close(): Promise<void>;
|
|
144
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
|
+
import { StreamableHTTPClientTransport, StreamableHTTPError } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
5
|
+
import { CallToolResultSchema, ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { providerStatus, resolveSecret } from './config.js';
|
|
7
|
+
import { SiftpathError } from './errors.js';
|
|
8
|
+
import { packageVersion } from './version.js';
|
|
9
|
+
export async function abortable(promise, signal) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const abort = () => reject(signal.reason);
|
|
12
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener('abort', abort));
|
|
13
|
+
if (signal.aborted)
|
|
14
|
+
abort();
|
|
15
|
+
else
|
|
16
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** One lazy connection per configured MCP, shared by all router calls in this process. */
|
|
20
|
+
export class McpClients {
|
|
21
|
+
config;
|
|
22
|
+
env;
|
|
23
|
+
connections = new Map();
|
|
24
|
+
closed = false;
|
|
25
|
+
closing;
|
|
26
|
+
requestSignal = new AsyncLocalStorage();
|
|
27
|
+
fetch = async (input, init) => {
|
|
28
|
+
const operation = this.requestSignal.getStore();
|
|
29
|
+
const signals = [init?.signal, init?.method === 'GET' ? undefined : operation].filter((s) => !!s);
|
|
30
|
+
const response = await globalThis.fetch(input, { ...init, signal: signals.length ? AbortSignal.any(signals) : undefined });
|
|
31
|
+
if (!response.body)
|
|
32
|
+
return response;
|
|
33
|
+
let bytes = 0;
|
|
34
|
+
const body = response.body.pipeThrough(new TransformStream({
|
|
35
|
+
transform(chunk, controller) {
|
|
36
|
+
bytes += chunk.byteLength;
|
|
37
|
+
if (bytes > 4 * 1024 * 1024) {
|
|
38
|
+
controller.error(new SiftpathError('RESPONSE_TOO_LARGE', 'Official MCP response exceeded 4 MiB.'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
controller.enqueue(chunk);
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
return new Response(body, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
45
|
+
};
|
|
46
|
+
constructor(config, env = process.env) {
|
|
47
|
+
this.config = config;
|
|
48
|
+
this.env = env;
|
|
49
|
+
}
|
|
50
|
+
get(id) {
|
|
51
|
+
if (this.closed)
|
|
52
|
+
throw new SiftpathError('CLOSED', 'Siftpath has been closed.');
|
|
53
|
+
const existing = this.connections.get(id);
|
|
54
|
+
if (existing)
|
|
55
|
+
return existing.ready;
|
|
56
|
+
const status = providerStatus(id, this.config, this.env);
|
|
57
|
+
if (status.code !== 'READY')
|
|
58
|
+
throw new SiftpathError(status.code, 'Provider is unavailable.', [status]);
|
|
59
|
+
const provider = this.config.providers[id], c = provider.connection;
|
|
60
|
+
const values = (entries) => Object.fromEntries(Object.entries(entries).map(([key, val]) => [key, resolveSecret(val, this.config, this.env)]));
|
|
61
|
+
const transport = c.transport === 'stdio'
|
|
62
|
+
? new StdioClientTransport({ command: c.command, args: c.args, env: { ...getDefaultEnvironment(), ...values(c.env) }, stderr: 'ignore', maxBufferSize: 4 * 1024 * 1024 })
|
|
63
|
+
: new StreamableHTTPClientTransport(new URL(c.url), { fetch: this.fetch, requestInit: { headers: values(c.headers), redirect: 'error' },
|
|
64
|
+
reconnectionOptions: { maxRetries: 0, initialReconnectionDelay: 1000, maxReconnectionDelay: 1000, reconnectionDelayGrowFactor: 1 } });
|
|
65
|
+
const client = new Client({ name: 'siftpath', version: packageVersion });
|
|
66
|
+
client.onerror = () => { }; // Upstream messages may contain credentials; expose only our stable error codes.
|
|
67
|
+
const timeout = provider.timeoutMs ?? this.config.providerTimeoutMs;
|
|
68
|
+
const deadline = AbortSignal.timeout(timeout);
|
|
69
|
+
const ready = abortable(this.requestSignal.run(deadline, () => client.connect(transport, { timeout, signal: deadline })), deadline).then(() => client).catch(async (error) => {
|
|
70
|
+
if (this.connections.get(id)?.client === client)
|
|
71
|
+
this.connections.delete(id);
|
|
72
|
+
await client.close().catch(() => { });
|
|
73
|
+
const code = deadline.aborted ? 'PROVIDER_TIMEOUT' : error instanceof StreamableHTTPError && [401, 403].includes(error.code ?? 0) ? 'AUTH_FAILED'
|
|
74
|
+
: error instanceof StreamableHTTPError && error.code === 429 ? 'RATE_LIMITED' : 'CONNECTION_FAILED';
|
|
75
|
+
throw new SiftpathError(code, 'Cannot connect to official MCP. Check configuration, credentials and connectivity.');
|
|
76
|
+
});
|
|
77
|
+
this.connections.set(id, { client, transport, ready });
|
|
78
|
+
// The SDK invokes onclose when the underlying transport exits/disconnects.
|
|
79
|
+
client.onclose = () => { if (this.connections.get(id)?.client === client)
|
|
80
|
+
this.connections.delete(id); };
|
|
81
|
+
return ready;
|
|
82
|
+
}
|
|
83
|
+
async connect(id, signal) {
|
|
84
|
+
await abortable(this.get(id), signal);
|
|
85
|
+
}
|
|
86
|
+
async listTools(id, signal, cursor) {
|
|
87
|
+
const client = await abortable(this.get(id), signal);
|
|
88
|
+
// SDK listTools replaces its metadata cache per page; diagnostics retain and validate the complete list themselves.
|
|
89
|
+
return this.requestSignal.run(signal, () => abortable(client.request({ method: 'tools/list', params: cursor ? { cursor } : {} }, ListToolsResultSchema, { signal,
|
|
90
|
+
timeout: this.config.providers[id].timeoutMs ?? this.config.providerTimeoutMs }), signal));
|
|
91
|
+
}
|
|
92
|
+
async call(id, name, args, signal) {
|
|
93
|
+
const client = await abortable(this.get(id), signal);
|
|
94
|
+
signal.throwIfAborted();
|
|
95
|
+
try {
|
|
96
|
+
const result = await this.requestSignal.run(signal, () => abortable(client.callTool({ name, arguments: args }, CallToolResultSchema, { signal,
|
|
97
|
+
timeout: this.config.providers[id].timeoutMs ?? this.config.providerTimeoutMs }), signal));
|
|
98
|
+
return CallToolResultSchema.parse(result);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
// Expired HTTP sessions do not emit onclose in the SDK. Reconnect on the next operation, not an automatic billable retry.
|
|
102
|
+
if (error instanceof StreamableHTTPError && error.code === 404) {
|
|
103
|
+
if (this.connections.get(id)?.client === client)
|
|
104
|
+
this.connections.delete(id);
|
|
105
|
+
await client.close().catch(() => { });
|
|
106
|
+
}
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async close() {
|
|
111
|
+
if (this.closing)
|
|
112
|
+
return this.closing;
|
|
113
|
+
this.closed = true;
|
|
114
|
+
const connections = [...this.connections.values()];
|
|
115
|
+
this.connections.clear();
|
|
116
|
+
this.closing = Promise.allSettled(connections.map(async ({ client, transport }) => {
|
|
117
|
+
try {
|
|
118
|
+
if (transport instanceof StreamableHTTPClientTransport && transport.sessionId) {
|
|
119
|
+
const deadline = AbortSignal.timeout(2000);
|
|
120
|
+
await this.requestSignal.run(deadline, () => abortable(transport.terminateSession(), deadline));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
await client.close();
|
|
125
|
+
}
|
|
126
|
+
})).then(() => { });
|
|
127
|
+
return this.closing;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|