@outfitter/mcp 0.2.0 → 0.3.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/README.md +39 -3
- package/dist/index.d.ts +49 -26
- package/dist/index.js +85 -33
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -54,9 +54,10 @@ Creates an MCP server instance.
|
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
56
|
interface McpServerOptions {
|
|
57
|
-
name: string;
|
|
58
|
-
version: string;
|
|
59
|
-
logger?: Logger;
|
|
57
|
+
name: string; // Server name for MCP handshake
|
|
58
|
+
version: string; // Server version (semver)
|
|
59
|
+
logger?: Logger; // Optional structured logger
|
|
60
|
+
defaultLogLevel?: McpLogLevel | null; // Default log forwarding level
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
const server = createMcpServer({
|
|
@@ -66,6 +67,41 @@ const server = createMcpServer({
|
|
|
66
67
|
});
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
### Log Forwarding
|
|
71
|
+
|
|
72
|
+
MCP servers can forward log messages to the connected client. The default log level is resolved from environment configuration:
|
|
73
|
+
|
|
74
|
+
**Precedence** (highest wins):
|
|
75
|
+
1. `OUTFITTER_LOG_LEVEL` environment variable
|
|
76
|
+
2. `options.defaultLogLevel`
|
|
77
|
+
3. `OUTFITTER_ENV` profile defaults (`"debug"` in development, `null` otherwise)
|
|
78
|
+
4. `null` (no forwarding)
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const server = createMcpServer({
|
|
82
|
+
name: "my-server",
|
|
83
|
+
version: "1.0.0",
|
|
84
|
+
// Forwarding level auto-resolved from OUTFITTER_ENV
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// With OUTFITTER_ENV=development → forwards at "debug"
|
|
88
|
+
// With OUTFITTER_ENV=production → no forwarding (null)
|
|
89
|
+
// With OUTFITTER_LOG_LEVEL=error → forwards at "error"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Set `defaultLogLevel: null` to explicitly disable forwarding regardless of environment. The MCP client can always override via `logging/setLevel`.
|
|
93
|
+
|
|
94
|
+
#### `sendLogMessage(level, data, loggerName?)`
|
|
95
|
+
|
|
96
|
+
Send a log message to the connected MCP client.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
server.sendLogMessage("info", "Indexing complete", "my-server");
|
|
100
|
+
server.sendLogMessage("warning", { message: "Rate limited", retryAfter: 30 });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Only sends if the message level meets or exceeds the current client log level threshold.
|
|
104
|
+
|
|
69
105
|
### defineTool(definition)
|
|
70
106
|
|
|
71
107
|
Helper for defining typed tools with better type inference.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,32 @@ import { ActionRegistry, ActionSurface, AnyActionSpec } from "@outfitter/contrac
|
|
|
2
2
|
import { Handler, HandlerContext, Logger, OutfitterError, Result, TaggedErrorClass } from "@outfitter/contracts";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
/**
|
|
5
|
+
* @outfitter/mcp - Logging Bridge
|
|
6
|
+
*
|
|
7
|
+
* Maps Outfitter log levels to MCP log levels for
|
|
8
|
+
* server-to-client log message notifications.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* MCP log levels as defined in the MCP specification.
|
|
14
|
+
* Ordered from least to most severe.
|
|
15
|
+
*/
|
|
16
|
+
type McpLogLevel = "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
|
|
17
|
+
/**
|
|
18
|
+
* Outfitter log levels.
|
|
19
|
+
*/
|
|
20
|
+
type OutfitterLogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
21
|
+
/**
|
|
22
|
+
* Map an Outfitter log level to the corresponding MCP log level.
|
|
23
|
+
*/
|
|
24
|
+
declare function mapLogLevelToMcp(level: OutfitterLogLevel): McpLogLevel;
|
|
25
|
+
/**
|
|
26
|
+
* Check whether a message at the given level should be emitted
|
|
27
|
+
* based on the client-requested threshold.
|
|
28
|
+
*/
|
|
29
|
+
declare function shouldEmitLog(messageLevel: McpLogLevel, threshold: McpLogLevel): boolean;
|
|
30
|
+
/**
|
|
5
31
|
* Configuration options for creating an MCP server.
|
|
6
32
|
*
|
|
7
33
|
* @example
|
|
@@ -31,6 +57,19 @@ interface McpServerOptions {
|
|
|
31
57
|
* If not provided, a no-op logger is used.
|
|
32
58
|
*/
|
|
33
59
|
logger?: Logger;
|
|
60
|
+
/**
|
|
61
|
+
* Default MCP log level for client-facing log forwarding.
|
|
62
|
+
*
|
|
63
|
+
* Precedence (highest wins):
|
|
64
|
+
* 1. `OUTFITTER_LOG_LEVEL` environment variable
|
|
65
|
+
* 2. This option
|
|
66
|
+
* 3. Environment profile (`OUTFITTER_ENV`)
|
|
67
|
+
* 4. `null` (no forwarding until client opts in)
|
|
68
|
+
*
|
|
69
|
+
* Set to `null` to explicitly disable forwarding regardless of environment.
|
|
70
|
+
* The MCP client can always override via `logging/setLevel`.
|
|
71
|
+
*/
|
|
72
|
+
defaultLogLevel?: McpLogLevel | null;
|
|
34
73
|
}
|
|
35
74
|
/**
|
|
36
75
|
* Behavioral hints for MCP tools.
|
|
@@ -560,6 +599,16 @@ interface McpServer {
|
|
|
560
599
|
*/
|
|
561
600
|
setLogLevel?(level: string): void;
|
|
562
601
|
/**
|
|
602
|
+
* Send a log message to connected clients.
|
|
603
|
+
* Filters by the client-requested log level threshold.
|
|
604
|
+
* No-op if no SDK server is bound or if the message is below the threshold.
|
|
605
|
+
*
|
|
606
|
+
* @param level - MCP log level for the message
|
|
607
|
+
* @param data - Log data (string, object, or any serializable value)
|
|
608
|
+
* @param loggerName - Optional logger name for client-side filtering
|
|
609
|
+
*/
|
|
610
|
+
sendLogMessage(level: McpLogLevel, data: unknown, loggerName?: string): void;
|
|
611
|
+
/**
|
|
563
612
|
* Bind the SDK server instance for notifications.
|
|
564
613
|
* Called internally by the transport layer.
|
|
565
614
|
* @param sdkServer - The MCP SDK Server instance
|
|
@@ -697,32 +746,6 @@ interface CoreToolsOptions {
|
|
|
697
746
|
type NormalizedQueryInput = Required<Pick<QueryToolInput, "q">> & Omit<QueryToolInput, "q">;
|
|
698
747
|
type CoreToolDefinition = ToolDefinition<DocsToolInput, DocsToolResponse> | ToolDefinition<ConfigToolInput, ConfigToolResponse> | ToolDefinition<QueryToolInput, QueryToolResponse>;
|
|
699
748
|
declare function createCoreTools(options?: CoreToolsOptions): CoreToolDefinition[];
|
|
700
|
-
/**
|
|
701
|
-
* @outfitter/mcp - Logging Bridge
|
|
702
|
-
*
|
|
703
|
-
* Maps Outfitter log levels to MCP log levels for
|
|
704
|
-
* server-to-client log message notifications.
|
|
705
|
-
*
|
|
706
|
-
* @packageDocumentation
|
|
707
|
-
*/
|
|
708
|
-
/**
|
|
709
|
-
* MCP log levels as defined in the MCP specification.
|
|
710
|
-
* Ordered from least to most severe.
|
|
711
|
-
*/
|
|
712
|
-
type McpLogLevel = "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
|
|
713
|
-
/**
|
|
714
|
-
* Outfitter log levels.
|
|
715
|
-
*/
|
|
716
|
-
type OutfitterLogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
717
|
-
/**
|
|
718
|
-
* Map an Outfitter log level to the corresponding MCP log level.
|
|
719
|
-
*/
|
|
720
|
-
declare function mapLogLevelToMcp(level: OutfitterLogLevel): McpLogLevel;
|
|
721
|
-
/**
|
|
722
|
-
* Check whether a message at the given level should be emitted
|
|
723
|
-
* based on the client-requested threshold.
|
|
724
|
-
*/
|
|
725
|
-
declare function shouldEmitLog(messageLevel: McpLogLevel, threshold: McpLogLevel): boolean;
|
|
726
749
|
import { z as z2 } from "zod";
|
|
727
750
|
/**
|
|
728
751
|
* JSON Schema representation.
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,43 @@
|
|
|
2
2
|
import { DEFAULT_REGISTRY_SURFACES } from "@outfitter/contracts";
|
|
3
3
|
|
|
4
4
|
// src/server.ts
|
|
5
|
+
import { getEnvironment, getEnvironmentDefaults } from "@outfitter/config";
|
|
5
6
|
import { generateRequestId, Result } from "@outfitter/contracts";
|
|
6
7
|
|
|
8
|
+
// src/logging.ts
|
|
9
|
+
var MCP_LEVEL_ORDER = [
|
|
10
|
+
"debug",
|
|
11
|
+
"info",
|
|
12
|
+
"notice",
|
|
13
|
+
"warning",
|
|
14
|
+
"error",
|
|
15
|
+
"critical",
|
|
16
|
+
"alert",
|
|
17
|
+
"emergency"
|
|
18
|
+
];
|
|
19
|
+
function mapLogLevelToMcp(level) {
|
|
20
|
+
switch (level) {
|
|
21
|
+
case "trace":
|
|
22
|
+
case "debug":
|
|
23
|
+
return "debug";
|
|
24
|
+
case "info":
|
|
25
|
+
return "info";
|
|
26
|
+
case "warn":
|
|
27
|
+
return "warning";
|
|
28
|
+
case "error":
|
|
29
|
+
return "error";
|
|
30
|
+
case "fatal":
|
|
31
|
+
return "emergency";
|
|
32
|
+
default: {
|
|
33
|
+
const _exhaustiveCheck = level;
|
|
34
|
+
return _exhaustiveCheck;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function shouldEmitLog(messageLevel, threshold) {
|
|
39
|
+
return MCP_LEVEL_ORDER.indexOf(messageLevel) >= MCP_LEVEL_ORDER.indexOf(threshold);
|
|
40
|
+
}
|
|
41
|
+
|
|
7
42
|
// src/schema.ts
|
|
8
43
|
function zodToJsonSchema(schema) {
|
|
9
44
|
return convertZodType(schema);
|
|
@@ -367,6 +402,40 @@ function createNoOpLogger() {
|
|
|
367
402
|
child: () => createNoOpLogger()
|
|
368
403
|
};
|
|
369
404
|
}
|
|
405
|
+
var VALID_MCP_LOG_LEVELS = new Set([
|
|
406
|
+
"debug",
|
|
407
|
+
"info",
|
|
408
|
+
"notice",
|
|
409
|
+
"warning",
|
|
410
|
+
"error",
|
|
411
|
+
"critical",
|
|
412
|
+
"alert",
|
|
413
|
+
"emergency"
|
|
414
|
+
]);
|
|
415
|
+
var DEFAULTS_TO_MCP = {
|
|
416
|
+
debug: "debug",
|
|
417
|
+
info: "info",
|
|
418
|
+
warn: "warning",
|
|
419
|
+
error: "error"
|
|
420
|
+
};
|
|
421
|
+
function resolveDefaultLogLevel(options) {
|
|
422
|
+
const envLogLevel = process.env["OUTFITTER_LOG_LEVEL"];
|
|
423
|
+
if (envLogLevel !== undefined && VALID_MCP_LOG_LEVELS.has(envLogLevel)) {
|
|
424
|
+
return envLogLevel;
|
|
425
|
+
}
|
|
426
|
+
if (options.defaultLogLevel !== undefined && (options.defaultLogLevel === null || VALID_MCP_LOG_LEVELS.has(options.defaultLogLevel))) {
|
|
427
|
+
return options.defaultLogLevel;
|
|
428
|
+
}
|
|
429
|
+
const env = getEnvironment();
|
|
430
|
+
const defaults = getEnvironmentDefaults(env);
|
|
431
|
+
if (defaults.logLevel !== null) {
|
|
432
|
+
const mapped = DEFAULTS_TO_MCP[defaults.logLevel];
|
|
433
|
+
if (mapped !== undefined) {
|
|
434
|
+
return mapped;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
370
439
|
function createMcpServer(options) {
|
|
371
440
|
const { name, version, logger: providedLogger } = options;
|
|
372
441
|
const logger = providedLogger ?? createNoOpLogger();
|
|
@@ -376,6 +445,7 @@ function createMcpServer(options) {
|
|
|
376
445
|
const prompts = new Map;
|
|
377
446
|
let sdkServer = null;
|
|
378
447
|
const subscriptions = new Set;
|
|
448
|
+
let clientLogLevel = resolveDefaultLogLevel(options);
|
|
379
449
|
function createHandlerContext(toolName, requestId, signal, progressToken) {
|
|
380
450
|
const ctx = {
|
|
381
451
|
requestId,
|
|
@@ -752,10 +822,25 @@ function createMcpServer(options) {
|
|
|
752
822
|
sdkServer?.sendPromptListChanged?.();
|
|
753
823
|
},
|
|
754
824
|
setLogLevel(level) {
|
|
825
|
+
clientLogLevel = level;
|
|
755
826
|
logger.debug("Client log level set", { level });
|
|
756
827
|
},
|
|
828
|
+
sendLogMessage(level, data, loggerName) {
|
|
829
|
+
if (!sdkServer || clientLogLevel === null || !shouldEmitLog(level, clientLogLevel)) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const params = {
|
|
833
|
+
level,
|
|
834
|
+
data
|
|
835
|
+
};
|
|
836
|
+
if (loggerName !== undefined) {
|
|
837
|
+
params.logger = loggerName;
|
|
838
|
+
}
|
|
839
|
+
sdkServer.sendLoggingMessage?.(params);
|
|
840
|
+
},
|
|
757
841
|
bindSdkServer(server2) {
|
|
758
842
|
sdkServer = server2;
|
|
843
|
+
clientLogLevel = resolveDefaultLogLevel(options);
|
|
759
844
|
logger.debug("SDK server bound for notifications");
|
|
760
845
|
},
|
|
761
846
|
async start() {
|
|
@@ -969,39 +1054,6 @@ function createCoreTools(options = {}) {
|
|
|
969
1054
|
defineQueryTool(options.query)
|
|
970
1055
|
];
|
|
971
1056
|
}
|
|
972
|
-
// src/logging.ts
|
|
973
|
-
var MCP_LEVEL_ORDER = [
|
|
974
|
-
"debug",
|
|
975
|
-
"info",
|
|
976
|
-
"notice",
|
|
977
|
-
"warning",
|
|
978
|
-
"error",
|
|
979
|
-
"critical",
|
|
980
|
-
"alert",
|
|
981
|
-
"emergency"
|
|
982
|
-
];
|
|
983
|
-
function mapLogLevelToMcp(level) {
|
|
984
|
-
switch (level) {
|
|
985
|
-
case "trace":
|
|
986
|
-
case "debug":
|
|
987
|
-
return "debug";
|
|
988
|
-
case "info":
|
|
989
|
-
return "info";
|
|
990
|
-
case "warn":
|
|
991
|
-
return "warning";
|
|
992
|
-
case "error":
|
|
993
|
-
return "error";
|
|
994
|
-
case "fatal":
|
|
995
|
-
return "emergency";
|
|
996
|
-
default: {
|
|
997
|
-
const _exhaustiveCheck = level;
|
|
998
|
-
return _exhaustiveCheck;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
function shouldEmitLog(messageLevel, threshold) {
|
|
1003
|
-
return MCP_LEVEL_ORDER.indexOf(messageLevel) >= MCP_LEVEL_ORDER.indexOf(threshold);
|
|
1004
|
-
}
|
|
1005
1057
|
// src/transport.ts
|
|
1006
1058
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1007
1059
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outfitter/mcp",
|
|
3
3
|
"description": "MCP server framework with typed tools for Outfitter",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
43
|
+
"@outfitter/config": "0.3.0",
|
|
43
44
|
"@outfitter/contracts": "0.2.0",
|
|
44
|
-
"@outfitter/logging": "0.
|
|
45
|
+
"@outfitter/logging": "0.3.0",
|
|
45
46
|
"zod": "^4.3.5"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|