@dotsetlabs/bellwether 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -31
- package/dist/cli/index.js +27 -34
- package/dist/constants/core.d.ts +2 -2
- package/dist/constants/core.js +2 -2
- package/dist/logging/logger.js +5 -2
- package/dist/transport/env-filter.d.ts +6 -0
- package/dist/transport/env-filter.js +76 -0
- package/dist/transport/mcp-client.d.ts +0 -9
- package/dist/transport/mcp-client.js +3 -84
- package/dist/version.js +2 -2
- package/man/bellwether.1 +1 -1
- package/man/bellwether.1.md +2 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -31,6 +31,15 @@ bellwether check
|
|
|
31
31
|
|
|
32
32
|
That's it. No API keys. No LLM costs. Runs in seconds.
|
|
33
33
|
|
|
34
|
+
## Product Focus
|
|
35
|
+
|
|
36
|
+
Bellwether is intentionally opinionated:
|
|
37
|
+
|
|
38
|
+
- **Core workflow (default)**: `init` -> `check` -> `baseline`
|
|
39
|
+
- **Advanced workflow (opt-in)**: `explore`, `watch`, `discover`, `golden`, `contract`, `registry`
|
|
40
|
+
|
|
41
|
+
If you only need CI-safe drift detection, you can stay entirely in the core workflow.
|
|
42
|
+
|
|
34
43
|
## Two Modes
|
|
35
44
|
|
|
36
45
|
| Mode | Purpose | Cost | When to Use |
|
|
@@ -85,45 +94,29 @@ jobs:
|
|
|
85
94
|
|
|
86
95
|
Comparisons are **protocol-version-aware** — version-specific fields (annotations, titles, output schemas, etc.) are only compared when both baselines support the relevant MCP protocol version.
|
|
87
96
|
|
|
88
|
-
##
|
|
89
|
-
|
|
90
|
-
### Essential Commands
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
bellwether init <server-command> # Create config
|
|
94
|
-
bellwether check # Detect drift (free, deterministic)
|
|
95
|
-
bellwether baseline save # Save baseline to compare against
|
|
96
|
-
bellwether baseline compare # Compare current vs saved baseline
|
|
97
|
-
```
|
|
97
|
+
## Command Tiers
|
|
98
98
|
|
|
99
|
-
###
|
|
99
|
+
### Core Commands (Recommended)
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
| Command | Purpose |
|
|
102
|
+
|:--------|:--------|
|
|
103
|
+
| `init` | Create `bellwether.yaml` |
|
|
104
|
+
| `check` | Deterministic schema drift detection |
|
|
105
|
+
| `baseline save` | Save snapshot for future comparisons |
|
|
106
|
+
| `baseline compare` | Compare latest check output to saved baseline |
|
|
107
107
|
|
|
108
|
-
###
|
|
108
|
+
### Advanced Commands (Optional)
|
|
109
109
|
|
|
110
110
|
| Command | Purpose |
|
|
111
111
|
|:--------|:--------|
|
|
112
|
-
| `
|
|
113
|
-
| `check` | Schema drift detection (free) |
|
|
114
|
-
| `explore` | LLM behavioral testing |
|
|
115
|
-
| `baseline save` | Save test results as baseline |
|
|
116
|
-
| `baseline compare` | Compare against baseline |
|
|
117
|
-
| `baseline show` | Display baseline contents |
|
|
118
|
-
| `baseline accept` | Accept drift as intentional |
|
|
119
|
-
| `baseline diff` | Compare two baselines |
|
|
120
|
-
| `discover` | Show server capabilities |
|
|
112
|
+
| `explore` | LLM behavioral testing and `AGENTS.md` generation |
|
|
121
113
|
| `watch` | Continuous checking on file changes |
|
|
114
|
+
| `discover` | Capability inspection without tests |
|
|
122
115
|
| `registry` | Search MCP Registry |
|
|
123
116
|
| `golden` | Golden output regression testing |
|
|
124
|
-
| `contract` | Contract validation
|
|
117
|
+
| `contract` | Contract validation and generation |
|
|
125
118
|
| `auth` | Manage LLM provider API keys |
|
|
126
|
-
| `validate-config` | Validate bellwether.yaml without running tests |
|
|
119
|
+
| `validate-config` | Validate `bellwether.yaml` without running tests |
|
|
127
120
|
|
|
128
121
|
## CI/CD Exit Codes
|
|
129
122
|
|
|
@@ -139,9 +132,9 @@ Requires LLM (Ollama for free local, or OpenAI/Anthropic). Generates `AGENTS.md`
|
|
|
139
132
|
## GitHub Action
|
|
140
133
|
|
|
141
134
|
```yaml
|
|
142
|
-
- uses: dotsetlabs/bellwether@v2.
|
|
135
|
+
- uses: dotsetlabs/bellwether@v2.1.1
|
|
143
136
|
with:
|
|
144
|
-
version: '2.
|
|
137
|
+
version: '2.1.1'
|
|
145
138
|
server-command: 'npx @mcp/your-server'
|
|
146
139
|
baseline-path: './bellwether-baseline.json'
|
|
147
140
|
fail-on-severity: 'warning'
|
|
@@ -170,8 +163,17 @@ bellwether init --preset local npx @mcp/server # Local Ollama (free)
|
|
|
170
163
|
**[docs.bellwether.sh](https://docs.bellwether.sh)** — Full reference for configuration and commands.
|
|
171
164
|
|
|
172
165
|
- [Quick Start](https://docs.bellwether.sh/quickstart)
|
|
166
|
+
- [Core vs Advanced](https://docs.bellwether.sh/concepts/core-vs-advanced)
|
|
173
167
|
- [CLI Reference](https://docs.bellwether.sh/cli/init)
|
|
174
168
|
- [CI/CD Integration](https://docs.bellwether.sh/guides/ci-cd)
|
|
169
|
+
- [Golden Paths](https://docs.bellwether.sh/guides/golden-paths)
|
|
170
|
+
- [Compatibility Policy](https://docs.bellwether.sh/concepts/compatibility-policy)
|
|
171
|
+
|
|
172
|
+
## Project Governance
|
|
173
|
+
|
|
174
|
+
- [Roadmap](./ROADMAP.md)
|
|
175
|
+
- [Changelog](./CHANGELOG.md)
|
|
176
|
+
- [Security Policy](./SECURITY.md)
|
|
175
177
|
|
|
176
178
|
## Community
|
|
177
179
|
|
package/dist/cli/index.js
CHANGED
|
@@ -86,42 +86,37 @@ Bellwether - MCP Server Validation & Documentation
|
|
|
86
86
|
const examples = `
|
|
87
87
|
Examples:
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
$ bellwether init
|
|
91
|
-
$ bellwether
|
|
92
|
-
$ bellwether
|
|
89
|
+
Core workflow (recommended):
|
|
90
|
+
$ bellwether init npx @mcp/my-server # Create bellwether.yaml
|
|
91
|
+
$ bellwether check # Free, deterministic drift detection
|
|
92
|
+
$ bellwether baseline save # Save baseline snapshot
|
|
93
|
+
$ bellwether check --fail-on-drift # CI/CD gating
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
$ bellwether
|
|
96
|
-
$ bellwether
|
|
97
|
-
$ bellwether
|
|
98
|
-
|
|
99
|
-
Explore behavior (LLM-powered):
|
|
100
|
-
$ bellwether explore npx @mcp/my-server # Generate AGENTS.md documentation
|
|
101
|
-
|
|
102
|
-
Discover server capabilities:
|
|
103
|
-
$ bellwether discover npx @mcp/server-postgres
|
|
104
|
-
|
|
105
|
-
Search MCP Registry:
|
|
106
|
-
$ bellwether registry filesystem
|
|
95
|
+
Advanced workflow (opt-in):
|
|
96
|
+
$ bellwether explore # LLM behavioral exploration
|
|
97
|
+
$ bellwether discover # Quick capability inspection
|
|
98
|
+
$ bellwether watch # Continuous checking
|
|
107
99
|
|
|
108
100
|
Documentation: https://docs.bellwether.sh
|
|
109
101
|
`;
|
|
110
102
|
program
|
|
111
103
|
.name('bellwether')
|
|
112
104
|
.description(`${banner}
|
|
113
|
-
|
|
105
|
+
Deterministic MCP drift detection with an optional advanced analysis layer.
|
|
114
106
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
watch
|
|
124
|
-
|
|
107
|
+
Core commands (default path):
|
|
108
|
+
init - Create bellwether.yaml configuration
|
|
109
|
+
check - Schema validation and drift detection (free, deterministic)
|
|
110
|
+
baseline - Save and compare baseline snapshots
|
|
111
|
+
|
|
112
|
+
Advanced commands (opt-in):
|
|
113
|
+
explore - LLM-powered behavioral exploration and documentation
|
|
114
|
+
discover - Quick capability discovery (no tests)
|
|
115
|
+
watch - Continuous checking on file changes
|
|
116
|
+
registry - Search the MCP Registry
|
|
117
|
+
golden - Golden output regression testing
|
|
118
|
+
contract - Contract validation (generate/validate/show)
|
|
119
|
+
auth - Manage LLM provider API keys
|
|
125
120
|
validate-config - Validate bellwether.yaml without running tests
|
|
126
121
|
|
|
127
122
|
For more information on a specific command, use:
|
|
@@ -155,19 +150,17 @@ For more information on a specific command, use:
|
|
|
155
150
|
}
|
|
156
151
|
})
|
|
157
152
|
.addHelpText('after', examples);
|
|
158
|
-
|
|
159
|
-
program.addHelpText('beforeAll', '\nCore Commands:');
|
|
160
|
-
// Core commands - check and explore
|
|
153
|
+
program.addCommand(initCommand.description('Create a new bellwether.yaml configuration file'));
|
|
161
154
|
program.addCommand(checkCommand.description('Check MCP server schema and detect drift (free, fast, deterministic)'));
|
|
155
|
+
program.addCommand(baselineCommand.description('Manage baselines for drift detection (save, compare, show, diff)'));
|
|
156
|
+
// Advanced commands
|
|
162
157
|
program.addCommand(exploreCommand.description('Explore MCP server behavior with LLM-powered testing'));
|
|
163
158
|
program.addCommand(watchCommand.description('Watch for MCP server changes and auto-check'));
|
|
164
159
|
program.addCommand(discoverCommand.description('Discover MCP server capabilities (tools, prompts, resources)'));
|
|
165
|
-
program.addCommand(initCommand.description('Create a new bellwether.yaml configuration file'));
|
|
166
|
-
program.addCommand(authCommand.description('Manage LLM provider API keys (keychain storage)'));
|
|
167
|
-
program.addCommand(baselineCommand.description('Manage baselines for drift detection (save, compare, show, diff)'));
|
|
168
160
|
program.addCommand(goldenCommand.description('Manage golden outputs for tool validation (save, compare, list, delete)'));
|
|
169
161
|
program.addCommand(registryCommand.description('Search the MCP Registry for servers'));
|
|
170
162
|
program.addCommand(contractCommand.description('Validate MCP servers against contract definitions (validate, generate, show)'));
|
|
163
|
+
program.addCommand(authCommand.description('Manage LLM provider API keys (keychain storage)'));
|
|
171
164
|
program.addCommand(validateConfigCommand.description('Validate bellwether.yaml configuration (no tests)'));
|
|
172
165
|
// Custom help formatting
|
|
173
166
|
program.configureHelp({
|
package/dist/constants/core.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export declare const TIMEOUTS: {
|
|
|
7
7
|
readonly WATCH_INTERVAL: 5000;
|
|
8
8
|
/** Server startup delay (500ms) */
|
|
9
9
|
readonly SERVER_STARTUP: 500;
|
|
10
|
-
/** Minimum server startup wait (
|
|
11
|
-
readonly MIN_SERVER_STARTUP_WAIT:
|
|
10
|
+
/** Minimum server startup wait (500ms) */
|
|
11
|
+
readonly MIN_SERVER_STARTUP_WAIT: 500;
|
|
12
12
|
/** Server ready poll interval (100ms) */
|
|
13
13
|
readonly SERVER_READY_POLL: 100;
|
|
14
14
|
/** Process shutdown SIGKILL timeout (5 seconds) */
|
package/dist/constants/core.js
CHANGED
|
@@ -7,8 +7,8 @@ export const TIMEOUTS = {
|
|
|
7
7
|
WATCH_INTERVAL: 5000,
|
|
8
8
|
/** Server startup delay (500ms) */
|
|
9
9
|
SERVER_STARTUP: 500,
|
|
10
|
-
/** Minimum server startup wait (
|
|
11
|
-
MIN_SERVER_STARTUP_WAIT:
|
|
10
|
+
/** Minimum server startup wait (500ms) */
|
|
11
|
+
MIN_SERVER_STARTUP_WAIT: 500,
|
|
12
12
|
/** Server ready poll interval (100ms) */
|
|
13
13
|
SERVER_READY_POLL: 100,
|
|
14
14
|
/** Process shutdown SIGKILL timeout (5 seconds) */
|
package/dist/logging/logger.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import pino from 'pino';
|
|
2
|
+
const IS_TEST_ENV = process.env.NODE_ENV === 'test' ||
|
|
3
|
+
process.env.VITEST === 'true' ||
|
|
4
|
+
process.env.VITEST_WORKER_ID !== undefined;
|
|
2
5
|
/**
|
|
3
6
|
* Default configuration.
|
|
4
7
|
* Default level is 'warn' to keep CLI output clean.
|
|
5
8
|
* Users can enable verbose output with --log-level info or --log-level debug.
|
|
6
9
|
*/
|
|
7
10
|
const DEFAULT_CONFIG = {
|
|
8
|
-
level: 'warn',
|
|
11
|
+
level: IS_TEST_ENV ? 'silent' : 'warn',
|
|
9
12
|
pretty: false,
|
|
10
13
|
timestamp: true,
|
|
11
14
|
};
|
|
@@ -50,7 +53,7 @@ export function createLogger(config = {}) {
|
|
|
50
53
|
*/
|
|
51
54
|
export function getLogger(name) {
|
|
52
55
|
if (!globalLogger) {
|
|
53
|
-
globalLogger = createLogger({ level:
|
|
56
|
+
globalLogger = createLogger({ level: DEFAULT_CONFIG.level });
|
|
54
57
|
}
|
|
55
58
|
if (name) {
|
|
56
59
|
return globalLogger.child({ component: name });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter sensitive variables from process.env before spawning subprocesses.
|
|
3
|
+
* Explicitly provided additional environment variables are still allowed.
|
|
4
|
+
*/
|
|
5
|
+
export declare function filterSpawnEnv(baseEnv: NodeJS.ProcessEnv, additionalEnv?: Record<string, string>): Record<string, string>;
|
|
6
|
+
//# sourceMappingURL=env-filter.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variables to filter out when spawning MCP server processes.
|
|
3
|
+
* These may contain sensitive credentials that should not be exposed.
|
|
4
|
+
*/
|
|
5
|
+
const FILTERED_ENV_VARS = new Set([
|
|
6
|
+
// LLM API keys
|
|
7
|
+
'OPENAI_API_KEY',
|
|
8
|
+
'ANTHROPIC_API_KEY',
|
|
9
|
+
'GOOGLE_API_KEY',
|
|
10
|
+
'AZURE_OPENAI_API_KEY',
|
|
11
|
+
'COHERE_API_KEY',
|
|
12
|
+
'HUGGINGFACE_API_KEY',
|
|
13
|
+
'REPLICATE_API_TOKEN',
|
|
14
|
+
// Provider credentials
|
|
15
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
16
|
+
'AWS_SESSION_TOKEN',
|
|
17
|
+
'AZURE_CLIENT_SECRET',
|
|
18
|
+
'GOOGLE_APPLICATION_CREDENTIALS',
|
|
19
|
+
// SCM/CI tokens
|
|
20
|
+
'GITHUB_TOKEN',
|
|
21
|
+
'GH_TOKEN',
|
|
22
|
+
'GITLAB_TOKEN',
|
|
23
|
+
'BITBUCKET_TOKEN',
|
|
24
|
+
'NPM_TOKEN',
|
|
25
|
+
'PYPI_TOKEN',
|
|
26
|
+
// Database credentials
|
|
27
|
+
'DATABASE_URL',
|
|
28
|
+
'DATABASE_PASSWORD',
|
|
29
|
+
'POSTGRES_PASSWORD',
|
|
30
|
+
'MYSQL_PASSWORD',
|
|
31
|
+
'REDIS_PASSWORD',
|
|
32
|
+
'MONGODB_URI',
|
|
33
|
+
// Application secrets
|
|
34
|
+
'COOKIE_SECRET',
|
|
35
|
+
'SESSION_SECRET',
|
|
36
|
+
'JWT_SECRET',
|
|
37
|
+
'ENCRYPTION_KEY',
|
|
38
|
+
'PRIVATE_KEY',
|
|
39
|
+
]);
|
|
40
|
+
/**
|
|
41
|
+
* Patterns for environment variable names that should be filtered.
|
|
42
|
+
* Matches common naming conventions for secrets.
|
|
43
|
+
*/
|
|
44
|
+
const FILTERED_ENV_PATTERNS = [
|
|
45
|
+
/_API_KEY$/i,
|
|
46
|
+
/_SECRET$/i,
|
|
47
|
+
/_TOKEN$/i,
|
|
48
|
+
/_PASSWORD$/i,
|
|
49
|
+
/_PRIVATE_KEY$/i,
|
|
50
|
+
/_CREDENTIALS$/i,
|
|
51
|
+
/^SECRET_/i,
|
|
52
|
+
/^PRIVATE_/i,
|
|
53
|
+
];
|
|
54
|
+
function isSensitiveEnvVar(name) {
|
|
55
|
+
if (FILTERED_ENV_VARS.has(name)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return FILTERED_ENV_PATTERNS.some((pattern) => pattern.test(name));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Filter sensitive variables from process.env before spawning subprocesses.
|
|
62
|
+
* Explicitly provided additional environment variables are still allowed.
|
|
63
|
+
*/
|
|
64
|
+
export function filterSpawnEnv(baseEnv, additionalEnv) {
|
|
65
|
+
const filtered = {};
|
|
66
|
+
for (const [key, value] of Object.entries(baseEnv)) {
|
|
67
|
+
if (value !== undefined && !isSensitiveEnvVar(key)) {
|
|
68
|
+
filtered[key] = value;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (additionalEnv) {
|
|
72
|
+
Object.assign(filtered, additionalEnv);
|
|
73
|
+
}
|
|
74
|
+
return filtered;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=env-filter.js.map
|
|
@@ -86,15 +86,6 @@ export declare class MCPClient {
|
|
|
86
86
|
*/
|
|
87
87
|
getTransportType(): TransportType;
|
|
88
88
|
private log;
|
|
89
|
-
/**
|
|
90
|
-
* Check if an environment variable name looks like a secret.
|
|
91
|
-
*/
|
|
92
|
-
private isSensitiveEnvVar;
|
|
93
|
-
/**
|
|
94
|
-
* Filter sensitive environment variables before passing to subprocess.
|
|
95
|
-
* Uses both explicit list and pattern matching to catch common secret naming conventions.
|
|
96
|
-
*/
|
|
97
|
-
private filterEnv;
|
|
98
89
|
/**
|
|
99
90
|
* Connect to an MCP server by spawning it as a subprocess.
|
|
100
91
|
*/
|
|
@@ -6,59 +6,7 @@ import { getLogger, startTiming } from '../logging/logger.js';
|
|
|
6
6
|
import { TIMEOUTS, MCP, TRANSPORT_ERRORS } from '../constants.js';
|
|
7
7
|
import { VERSION } from '../version.js';
|
|
8
8
|
import { getFeatureFlags, isKnownProtocolVersion, } from '../protocol/index.js';
|
|
9
|
-
|
|
10
|
-
* Environment variables to filter out when spawning MCP server processes.
|
|
11
|
-
* These may contain sensitive credentials that should not be exposed.
|
|
12
|
-
*/
|
|
13
|
-
const FILTERED_ENV_VARS = new Set([
|
|
14
|
-
// LLM API keys
|
|
15
|
-
'OPENAI_API_KEY',
|
|
16
|
-
'ANTHROPIC_API_KEY',
|
|
17
|
-
'GOOGLE_API_KEY',
|
|
18
|
-
'AZURE_OPENAI_API_KEY',
|
|
19
|
-
'COHERE_API_KEY',
|
|
20
|
-
'HUGGINGFACE_API_KEY',
|
|
21
|
-
'REPLICATE_API_TOKEN',
|
|
22
|
-
// Provider credentials
|
|
23
|
-
'AWS_SECRET_ACCESS_KEY',
|
|
24
|
-
'AWS_SESSION_TOKEN',
|
|
25
|
-
'AZURE_CLIENT_SECRET',
|
|
26
|
-
'GOOGLE_APPLICATION_CREDENTIALS',
|
|
27
|
-
// SCM/CI tokens
|
|
28
|
-
'GITHUB_TOKEN',
|
|
29
|
-
'GH_TOKEN',
|
|
30
|
-
'GITLAB_TOKEN',
|
|
31
|
-
'BITBUCKET_TOKEN',
|
|
32
|
-
'NPM_TOKEN',
|
|
33
|
-
'PYPI_TOKEN',
|
|
34
|
-
// Database credentials
|
|
35
|
-
'DATABASE_URL',
|
|
36
|
-
'DATABASE_PASSWORD',
|
|
37
|
-
'POSTGRES_PASSWORD',
|
|
38
|
-
'MYSQL_PASSWORD',
|
|
39
|
-
'REDIS_PASSWORD',
|
|
40
|
-
'MONGODB_URI',
|
|
41
|
-
// Application secrets
|
|
42
|
-
'COOKIE_SECRET',
|
|
43
|
-
'SESSION_SECRET',
|
|
44
|
-
'JWT_SECRET',
|
|
45
|
-
'ENCRYPTION_KEY',
|
|
46
|
-
'PRIVATE_KEY',
|
|
47
|
-
]);
|
|
48
|
-
/**
|
|
49
|
-
* Patterns for environment variable names that should be filtered.
|
|
50
|
-
* Matches common naming conventions for secrets.
|
|
51
|
-
*/
|
|
52
|
-
const FILTERED_ENV_PATTERNS = [
|
|
53
|
-
/_API_KEY$/i,
|
|
54
|
-
/_SECRET$/i,
|
|
55
|
-
/_TOKEN$/i,
|
|
56
|
-
/_PASSWORD$/i,
|
|
57
|
-
/_PRIVATE_KEY$/i,
|
|
58
|
-
/_CREDENTIALS$/i,
|
|
59
|
-
/^SECRET_/i,
|
|
60
|
-
/^PRIVATE_/i,
|
|
61
|
-
];
|
|
9
|
+
import { filterSpawnEnv } from './env-filter.js';
|
|
62
10
|
const DEFAULT_TIMEOUT = TIMEOUTS.DEFAULT;
|
|
63
11
|
const DEFAULT_STARTUP_DELAY = TIMEOUTS.SERVER_STARTUP;
|
|
64
12
|
/**
|
|
@@ -247,35 +195,6 @@ export class MCPClient {
|
|
|
247
195
|
this.logger.debug({ args }, 'MCP Debug');
|
|
248
196
|
}
|
|
249
197
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Check if an environment variable name looks like a secret.
|
|
252
|
-
*/
|
|
253
|
-
isSensitiveEnvVar(name) {
|
|
254
|
-
// Check explicit list
|
|
255
|
-
if (FILTERED_ENV_VARS.has(name)) {
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
// Check patterns
|
|
259
|
-
return FILTERED_ENV_PATTERNS.some((pattern) => pattern.test(name));
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Filter sensitive environment variables before passing to subprocess.
|
|
263
|
-
* Uses both explicit list and pattern matching to catch common secret naming conventions.
|
|
264
|
-
*/
|
|
265
|
-
filterEnv(baseEnv, additionalEnv) {
|
|
266
|
-
const filtered = {};
|
|
267
|
-
// Copy process.env, filtering out sensitive variables
|
|
268
|
-
for (const [key, value] of Object.entries(baseEnv)) {
|
|
269
|
-
if (value !== undefined && !this.isSensitiveEnvVar(key)) {
|
|
270
|
-
filtered[key] = value;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
// Add additional env vars (these are explicitly provided, so allow them)
|
|
274
|
-
if (additionalEnv) {
|
|
275
|
-
Object.assign(filtered, additionalEnv);
|
|
276
|
-
}
|
|
277
|
-
return filtered;
|
|
278
|
-
}
|
|
279
198
|
/**
|
|
280
199
|
* Connect to an MCP server by spawning it as a subprocess.
|
|
281
200
|
*/
|
|
@@ -291,7 +210,7 @@ export class MCPClient {
|
|
|
291
210
|
args,
|
|
292
211
|
};
|
|
293
212
|
// Filter out sensitive environment variables before spawning subprocess
|
|
294
|
-
const filteredEnv =
|
|
213
|
+
const filteredEnv = filterSpawnEnv(process.env, env);
|
|
295
214
|
this.process = spawn(command, args, {
|
|
296
215
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
297
216
|
env: filteredEnv,
|
|
@@ -418,7 +337,7 @@ export class MCPClient {
|
|
|
418
337
|
*/
|
|
419
338
|
async waitForStartup() {
|
|
420
339
|
// Enforce minimum startup delay to allow server to fully start
|
|
421
|
-
//
|
|
340
|
+
// while still honoring explicit higher startupDelay values from config/tests.
|
|
422
341
|
const delay = Math.max(this.startupDelay, TIMEOUTS.MIN_SERVER_STARTUP_WAIT);
|
|
423
342
|
this.logger.debug({ delay }, 'Waiting for server startup');
|
|
424
343
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
package/dist/version.js
CHANGED
|
@@ -29,8 +29,8 @@ function getPackageVersion() {
|
|
|
29
29
|
return packageJson.version;
|
|
30
30
|
}
|
|
31
31
|
catch {
|
|
32
|
-
//
|
|
33
|
-
return '
|
|
32
|
+
// Final fallback to avoid hardcoded release drift.
|
|
33
|
+
return process.env.BELLWETHER_VERSION || '0.0.0';
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
/**
|
package/man/bellwether.1
CHANGED
package/man/bellwether.1.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotsetlabs/bellwether",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "The open-source MCP testing tool. Structural drift detection and behavioral documentation for Model Context Protocol servers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc",
|
|
26
|
-
"postbuild": "
|
|
26
|
+
"postbuild": "node ./scripts/postbuild.mjs",
|
|
27
27
|
"dev": "tsc --watch",
|
|
28
28
|
"test": "vitest run",
|
|
29
29
|
"test:watch": "vitest",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
33
33
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
34
34
|
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
35
|
+
"check:consistency": "node ./scripts/validate-consistency.mjs",
|
|
35
36
|
"clean": "rm -rf dist",
|
|
36
37
|
"docs:generate": "npm --prefix website run build",
|
|
37
38
|
"docs:dev": "npm --prefix website run start",
|