Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ Returned content format:
- For each entry in urlList, the server loads its content, prefixes it with a header like: `# Documentation from <resolved-path-or-url>` and joins multiple entries using a separator: `\n\n---\n\n`.
- If an entry fails to load, an inline error message is included for that entry.

## Logging

The server uses a `diagnostics_channel`–based logger that keeps STDIO stdout pure by default. No terminal output occurs unless you enable a sink.

- Defaults: `level='info'`, `stderr=false`, `protocol=false`
- Sinks (opt‑in): `--log-stderr`, `--log-protocol` (forwards to MCP clients; requires advertising `capabilities.logging`)
- Transport tag: `transport: 'stdio' | 'http'` (no I/O side effects)
- Environment variables: not used for logging in this version
- Process scope: logger is process‑global; recommend one server per process

CLI examples:

```bash
patternfly-mcp # default (no terminal output)
patternfly-mcp --verbose # level=debug (still no stderr)
patternfly-mcp --log-stderr # enable stderr sink
patternfly-mcp --log-level warn --log-stderr
patternfly-mcp --log-protocol --log-level info # forward logs to MCP clients
```

Programmatic:

```ts
await start({ logging: { level: 'info', stderr: false, protocol: false } });
```

### Tool: usePatternFlyDocs

Use this to fetch high-level index content (for example, a local README.md that contains relevant links, or llms.txt files in docs-host mode). From that content, you can select specific URLs to pass to fetchDocs.
Expand Down
9 changes: 7 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export default [
varsIgnorePattern: '^_'
}],
'n/no-process-exit': 0,
'no-console': 0
// Disallow console.log/info in runtime to protect STDIO; allow warn/error
'no-console': ['error', { allow: ['warn', 'error'] }]
}
},

Expand All @@ -73,7 +74,11 @@ export default [
rules: {
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/ban-ts-comment': 1,
'no-sparse-arrays': 0
'no-sparse-arrays': 0,
// Allow console usage in tests (spies, debug)
'no-console': 0,
// Relax stylistic padding in tests to reduce churn
'@stylistic/padding-line-between-statements': 0
}
}
];
5 changes: 5 additions & 0 deletions jest.setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Shared helpers for Jest unit tests

/**
* Set NODE_ENV to 'local' for local testing.
*/
process.env.NODE_ENV = 'local';

/**
* Note: Mock @patternfly/patternfly-component-schemas/json to avoid top-level await issues in Jest
* - Individual tests can override mock
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
"build:clean": "rm -rf dist",
"build:watch": "npm run build -- --watch",
"release": "changelog --non-cc --link-url https://github.com/patternfly/patternfly-mcp.git",
"start": "node dist/index.js",
"start": "node dist/index.js --verbose --log-stderr",
"start:dev": "tsx watch src/index.ts",
"test": "export NODE_ENV=local; npm run test:lint && npm run test:types && jest --roots=src/",
"test": "npm run test:lint && npm run test:types && jest --roots=src/",
"test:dev": "npm test -- --watchAll",
"test:integration": "npm run build && jest --roots=tests/",
"test:integration-dev": "npm run test:integration -- --watchAll",
Expand Down
20 changes: 20 additions & 0 deletions src/__tests__/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ exports[`main should merge default, cli and programmatic options, merge programm
[
{
"docsHost": true,
"logging": {
"level": "info",
"protocol": false,
"stderr": false,
},
},
],
],
Expand All @@ -24,6 +29,11 @@ exports[`main should merge default, cli and programmatic options, merge programm
[
{
"docsHost": true,
"logging": {
"level": "info",
"protocol": false,
"stderr": false,
},
},
],
],
Expand All @@ -42,6 +52,11 @@ exports[`main should merge default, cli and programmatic options, with empty pro
[
{
"docsHost": true,
"logging": {
"level": "info",
"protocol": false,
"stderr": false,
},
},
],
],
Expand All @@ -60,6 +75,11 @@ exports[`main should merge default, cli and programmatic options, with undefined
[
{
"docsHost": false,
"logging": {
"level": "info",
"protocol": false,
"stderr": false,
},
},
],
],
Expand Down
197 changes: 197 additions & 0 deletions src/__tests__/__snapshots__/logger.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`createLogger should activate stderr subscriber writes only at or above level: stderr 1`] = `
[
[
"[INFO]: lorem ipsum :123 {"a":1}
",
],
]
`;

exports[`createLogger should attempt to subscribe and unsubscribe from a channel, with no logging options: subscribe 1`] = `
{
"subscribe": [],
"unsubscribe": [],
}
`;

exports[`createLogger should attempt to subscribe and unsubscribe from a channel, with stderr, and emulated channel to pass checks: subscribe 1`] = `
{
"subscribe": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
[Function],
],
],
"unsubscribe": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
[Function],
],
],
}
`;

exports[`logSeverity should return log severity, debug 1`] = `0`;

exports[`logSeverity should return log severity, default 1`] = `-1`;

exports[`logSeverity should return log severity, error 1`] = `3`;

exports[`logSeverity should return log severity, info 1`] = `1`;

exports[`logSeverity should return log severity, warn 1`] = `2`;

exports[`publish should attempt to create a log entry, args 1`] = `
{
"channel": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
],
],
"publish": [
[
{
"args": [
"dolor",
"sit",
"amet",
],
"level": "info",
"msg": "lorem ipsum, info",
"time": 1761955200000,
"transport": "stdio",
},
],
],
}
`;

exports[`publish should attempt to create a log entry, channel name 1`] = `
{
"channel": [
[
"custom-channel",
],
],
"publish": [
[
{
"args": [
"dolor",
"sit",
"amet",
],
"level": "info",
"msg": "lorem ipsum, info",
"time": 1761955200000,
"transport": undefined,
},
],
],
}
`;

exports[`publish should attempt to create a log entry, default 1`] = `
{
"channel": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
],
],
"publish": [
[
{
"level": undefined,
"time": 1761955200000,
"transport": "stdio",
},
],
],
}
`;

exports[`publish should attempt to create a log entry, level 1`] = `
{
"channel": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
],
],
"publish": [
[
{
"level": "info",
"time": 1761955200000,
"transport": "stdio",
},
],
],
}
`;

exports[`publish should attempt to create a log entry, msg 1`] = `
{
"channel": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
],
],
"publish": [
[
{
"level": "info",
"msg": "lorem ipsum, info",
"time": 1761955200000,
"transport": "stdio",
},
],
],
}
`;

exports[`registerStderrSubscriber should activate stderr subscriber writes only at or above level: stderr 1`] = `
[
[
"[INFO]: lorem ipsum :123 {"a":1}
",
],
]
`;

exports[`registerStderrSubscriber should attempt to subscribe and unsubscribe from a channel: subscribe 1`] = `
{
"subscribe": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
[Function],
],
],
"unsubscribe": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
[Function],
],
],
}
`;

exports[`subscribeToChannel should attempt to subscribe and unsubscribe from a channel: subscribe 1`] = `
{
"subscribe": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
[Function],
],
],
"unsubscribe": [
[
"pf-mcp:log:1234d567-1ce9-123d-1413-a1234e56c789",
[Function],
],
],
}
`;

exports[`subscribeToChannel should throw an error attempting to subscribe and unsubscribe from a channel: missing channel name 1`] = `"subscribeToChannel called without a configured logging channelName"`;
Loading
Loading