-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathindex.ts
More file actions
218 lines (197 loc) · 6.05 KB
/
index.ts
File metadata and controls
218 lines (197 loc) · 6.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import { parseCliOptions, type CliOptions, type DefaultOptionsOverrides } from './options';
import { getSessionOptions, setOptions, runWithSession } from './options.context';
import {
runServer,
type ServerInstance,
type ServerSettings,
type ServerOnLog,
type ServerOnLogHandler,
type ServerLogEvent,
type ServerStatReport,
type ServerStats,
type ServerGetStats,
type ServerOptions
} from './server';
import {
createMcpTool,
type ToolCreator,
type ToolModule,
type ToolConfig,
type ToolMultiConfig,
type ToolExternalOptions,
type ToolInternalOptions
} from './server.toolsUser';
/**
* Options for "programmatic" use. Extends the `DefaultOptions` interface.
*/
type PfMcpOptions = DefaultOptionsOverrides;
/**
* Additional settings for programmatic control.
*
* @property {boolean} allowProcessExit - Override process exits. Useful for tests
* or programmatic use to avoid exiting.
* - Setting directly overrides `mode` property defaults.
* - When `mode=cli` or `mode=programmatic` or `undefined`, defaults to `true`.
* - When `mode=test`, defaults to `false`.
*/
type PfMcpSettings = Pick<ServerSettings, 'allowProcessExit'>;
/**
* Server instance with shutdown capability
*
* @alias ServerInstance
*/
type PfMcpInstance = ServerInstance;
/**
* Subscribes a handler function, `PfMcpOnLogHandler`, to server logs. Automatically unsubscribed on server shutdown.
*
* @alias ServerOnLog
*/
type PfMcpOnLog = ServerOnLog;
/**
* The handler function passed by `onLog`, `PfMcpOnLog`, to subscribe to server logs. Automatically unsubscribed on server shutdown.
*
* @alias ServerOnLogHandler
*/
type PfMcpOnLogHandler = ServerOnLogHandler;
/**
* The log event passed to the `onLog` handler, `PfMcpOnLogHandler`.
*
* @alias ServerLogEvent
*/
type PfMcpLogEvent = ServerLogEvent;
/**
* Get statistics about the server.
*
* @alias ServerGetStats
*/
type PfMcpGetStats = ServerGetStats;
/**
* Statistics about the server.
*
* @alias ServerStats
*/
type PfMcpStats = ServerStats;
/**
* Statistics report about the server.
*
* @alias ServerStatReport
*/
type PfMcpStatReport = ServerStatReport;
/**
* Main function - Programmatic and CLI entry point with optional overrides
*
* @param [pfMcpOptions] - User configurable options
* @param [pfMcpSettings] - MCP server settings
*
* @returns {Promise<PfMcpInstance>} Server-instance with shutdown capability
*
* @throws {Error} If `allowProcessExit` is set to `false` an error will be thrown rather than exiting
* the process. Server errors are noted as options or start failures.
*
* @example Programmatic: A MCP server with STDIO (Standard Input Output) transport.
* import { start } from '@patternfly/patternfly-mcp';
* const { stop, isRunning } = await start();
*
* if (isRunning()) {
* stop();
* }
*
* @example Programmatic: A MCP server with HTTP transport.
* import { start } from '@patternfly/patternfly-mcp';
* const { stop, isRunning } = await start({ http: { port: 8000 } });
*
* if (isRunning()) {
* stop();
* }
*
* @example Programmatic: Listening for server stats
* import { subscribe, unsubscribe } from 'node:diagnostics_channel';
* import { start, createMcpTool } from '@patternfly/patternfly-mcp';
*
* const { stop, isRunning, getStats } = await start();
* const stats = await getStats();
* const statsChannel = subscribe(stats.health.channelId, (healthStats: PfMcpHealthStats) => {
* stderr.write(`Health uptime: ${healthStats.uptime}\n`);
* })
*
* if (isRunning()) {
* unsubscribe(stats.health.channelId);
* stop();
* }
*
* @example Programmatic: A MCP server with inline tool configuration.
* import { start, createMcpTool } from '@patternfly/patternfly-mcp';
*
* const myToolModule = createMcpTool({
* name: 'my-tool',
* description: 'My tool description',
* inputSchema: {},
* handler: async (args) => args
* });
*
* const { stop, isRunning } = await start({ toolModules: [myToolModule] });
*
* if (isRunning()) {
* stop();
* }
*/
const main = async (
pfMcpOptions: PfMcpOptions = {},
pfMcpSettings: PfMcpSettings = {}
): Promise<PfMcpInstance> => {
const { mode: programmaticMode, ...options } = pfMcpOptions;
const { allowProcessExit } = pfMcpSettings;
// Check early for allowing process exits
let updatedAllowProcessExit = allowProcessExit ?? programmaticMode !== 'test';
let mergedOptions: ServerOptions;
// If allowed, exit the process on error otherwise log then throw the error.
const processExit = (message: string, error: unknown) => {
console.error(message, error);
if (updatedAllowProcessExit) {
process.exit(1);
}
throw error;
};
try {
// Parse CLI options
const { mode: cliMode, ...cliOptions } = parseCliOptions();
// Apply `mode` separately because `cli.ts` applies it programmatically. Doing this allows us to set mode through `CLI options`.
mergedOptions = setOptions({ ...cliOptions, ...options, mode: cliMode ?? programmaticMode });
// Finalize exit policy after merging options
updatedAllowProcessExit = allowProcessExit ?? mergedOptions.mode !== 'test';
} catch (error) {
processExit('Set options error, failed to start server:', error);
}
try {
// Generate session options
const session = getSessionOptions();
// Start the server, apply session values, then apply merged options to ensure stable hashing.
return await runWithSession(session, async () =>
await runServer.memo(mergedOptions, { allowProcessExit: updatedAllowProcessExit }));
} catch (error) {
processExit('Failed to start server:', error);
}
// Unreachable, processExit exits or throws. Kept for type satisfaction.
return undefined as never;
};
export {
createMcpTool,
main,
main as start,
type CliOptions,
type PfMcpOptions,
type PfMcpSettings,
type PfMcpInstance,
type PfMcpLogEvent,
type PfMcpOnLog,
type PfMcpOnLogHandler,
type PfMcpStatReport,
type PfMcpStats,
type PfMcpGetStats,
type ToolCreator,
type ToolModule,
type ToolConfig,
type ToolMultiConfig,
type ToolExternalOptions,
type ToolInternalOptions
};