Skip to content

Commit b10f0e4

Browse files
committed
example and more documentation
1 parent b17d617 commit b10f0e4

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

Diff for: src/examples/server/simpleStreamableHttp.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@ const server = new McpServer({
8686
version: '1.0.0',
8787
}, { capabilities: { logging: {} } });
8888

89+
// Log the capability invocation details
90+
server.onCapabilityChange((event) => {
91+
switch (event.action) {
92+
case 'invoked':
93+
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' started`);
94+
break;
95+
case 'completed':
96+
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' completed in ${event.durationMs}ms`);
97+
break;
98+
case 'error':
99+
console.log(`${event.capabilityType} invocation ${event.invocationIndex}: '${event.capabilityName}' failed in ${event.durationMs}ms: ${event.error}`);
100+
break;
101+
}
102+
});
103+
89104
// Register a simple tool that returns a greeting
90105
server.tool(
91106
'greet',
@@ -324,7 +339,7 @@ function isInitializeRequest(body: unknown): boolean {
324339
const PORT = 3000;
325340
app.listen(PORT, () => {
326341
console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
327-
console.log(`Initialize session with the command below id you are using curl for testing:
342+
console.log(`Initialize session with the command below id you are using curl for testing:
328343
-----------------------------
329344
SESSION_ID=$(curl -X POST \
330345
-H "Content-Type: application/json" \
@@ -335,7 +350,7 @@ app.listen(PORT, () => {
335350
"method": "initialize",
336351
"params": {
337352
"capabilities": {},
338-
"protocolVersion": "2025-03-26",
353+
"protocolVersion": "2025-03-26",
339354
"clientInfo": {
340355
"name": "test",
341356
"version": "1.0.0"
@@ -353,4 +368,4 @@ process.on('SIGINT', async () => {
353368
console.log('Shutting down server...');
354369
await server.close();
355370
process.exit(0);
356-
});
371+
});

Diff for: src/server/mcp.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ export class McpServer {
6565
private _registeredPrompts: { [name: string]: RegisteredPrompt } = {};
6666

6767
private _onCapabilityChange = createEventNotifier<CapabilityEvent>();
68+
/** Counter for unique resource invocation indexes, used to correlate resource invocation events */
6869
private _resourceInvocationIndex = 0;
70+
/** Counter for unique tool invocation indexes, used to correlate tool invocation events */
6971
private _toolInvocationIndex = 0;
72+
/** Counter for unique prompt invocation indexes, used to correlate prompt invocation events */
7073
private _promptInvocationIndex = 0;
7174

7275
constructor(serverInfo: Implementation, options?: ServerOptions) {
@@ -94,9 +97,18 @@ export class McpServer {
9497
* Event notifier for capability changes. Listeners will be notified when capabilities are added, updated, removed,
9598
* enabled, disabled, invoked, completed, or when errors occur.
9699
*
100+
* This provides a way to monitor and respond to all capability-related activities in the server,
101+
* including both lifecycle changes and invocation events. Each capability type (resource, tool, prompt)
102+
* maintains its own sequence of invocation indexes, which can be used to correlate invocations
103+
* with their completions or errors.
104+
*
97105
* @example
98106
* const subscription = server.onCapabilityChange((event) => {
99-
* console.log(`${event.capabilityType} ${event.capabilityName} ${event.action}`);
107+
* if (event.action === "invoked") {
108+
* console.log(`${event.capabilityType} ${event.capabilityName} invoked with index ${event.invocationIndex}`);
109+
* } else if (event.action === "completed" || event.action === "error") {
110+
* console.log(`${event.capabilityType} operation completed in ${event.durationMs}ms`);
111+
* }
100112
* });
101113
*
102114
* // Later, to stop listening:
@@ -1799,29 +1811,71 @@ const EMPTY_COMPLETION_RESULT: CompleteResult = {
17991811
},
18001812
};
18011813

1814+
/**
1815+
* Represents events emitted when capabilities (tools, resources, prompts) change state or are invoked.
1816+
*
1817+
* These events allow tracking the lifecycle and usage of all capabilities registered with an McpServer.
1818+
* Events include capability registration, updates, invocation, completion, and errors.
1819+
*
1820+
* Each capability type (tool, resource, prompt) maintains its own sequence of invocation indexes.
1821+
* The invocationIndex can be used to correlate "invoked" events with their corresponding
1822+
* "completed" or "error" events for the same capability type.
1823+
*/
18021824
export type CapabilityEvent = {
1825+
/** Information about the server that generated this event */
18031826
readonly serverInfo: { readonly name: string; readonly version: string };
1827+
/** The type of capability this event relates to */
18041828
readonly capabilityType: "resource" | "tool" | "prompt";
1829+
/** The name (or URI for resources) of the specific capability */
18051830
readonly capabilityName: string;
18061831
} & (
18071832
| {
1833+
/**
1834+
* Lifecycle events for capability registration and status changes.
1835+
* - "added": The capability was registered
1836+
* - "updated": The capability was modified
1837+
* - "removed": The capability was unregistered
1838+
* - "enabled": The capability was enabled
1839+
* - "disabled": The capability was disabled
1840+
*/
18081841
readonly action: "added" | "updated" | "removed" | "enabled" | "disabled";
18091842
}
18101843
| {
1844+
/** Emitted when a capability is invoked */
18111845
readonly action: "invoked";
1846+
/**
1847+
* Monotonically increasing index for each invocation, per capability type.
1848+
* This index can be used to correlate this "invoked" event with a later
1849+
* "completed" or "error" event with the same capabilityType and invocationIndex.
1850+
*/
18121851
readonly invocationIndex: number;
1852+
/** The arguments passed to the capability, if any */
18131853
readonly arguments?: unknown;
18141854
}
18151855
| {
1856+
/** Emitted when a capability invocation completes successfully */
18161857
readonly action: "completed";
1858+
/**
1859+
* The invocationIndex from the corresponding "invoked" event.
1860+
* This allows correlating the completion with its invocation.
1861+
*/
18171862
readonly invocationIndex: number;
1863+
/** The result returned by the capability, if any */
18181864
readonly result?: unknown;
1865+
/** The duration of the operation in milliseconds, measured from invocation to completion */
18191866
readonly durationMs?: number;
18201867
}
18211868
| {
1869+
/** Emitted when a capability invocation fails with an error */
18221870
readonly action: "error";
1871+
/**
1872+
* The invocationIndex from the corresponding "invoked" event.
1873+
* This allows correlating the error with its invocation.
1874+
*/
18231875
readonly invocationIndex: number;
1876+
/** The error that occurred during capability execution */
18241877
readonly error: unknown;
1878+
/** The duration from invocation to error in milliseconds */
18251879
readonly durationMs?: number;
18261880
}
18271881
);

Diff for: src/shared/eventNotifier.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export type EventNotifier<T, E = Error> = {
3535
* ```
3636
*
3737
* @param listener A function that will be called with the notified event
38-
* @returns A SyncCloseable that, when closed, will unregister the listener
38+
* @returns A closeable to unregister the listener (all listeners are unregistered when the notifier is closed).
3939
*/
4040
onEvent: (listener: (event: T) => unknown) => { close: () => void };
4141

@@ -69,7 +69,7 @@ export type EventNotifier<T, E = Error> = {
6969
/**
7070
* Sets an error handler for the notifier. This handler will be called when a listener throws an error.
7171
*
72-
* @param handler A function that will be called with any errors thrown by listeners
72+
* @param handler A function that will be called with any errors thrown by listeners.
7373
*/
7474
onError: (handler: (error: E) => void) => void;
7575

@@ -101,7 +101,7 @@ export type EventNotifier<T, E = Error> = {
101101
*
102102
* @template T The type of events this notifier will handle
103103
* @template E The type of error that can be handled (defaults to Error)
104-
* @returns A new EventNotifier instance
104+
* @returns A new EventNotifier instance.
105105
*/
106106
export const createEventNotifier = <T, E = Error>(): EventNotifier<T, E> => {
107107
const listeners = new Set<(event: T) => unknown>();

0 commit comments

Comments
 (0)