@smithery/sdk 1.3.4 → 1.4.2
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/dist/server/stateful.d.ts +14 -3
- package/dist/server/stateful.js +9 -15
- package/dist/server/stateless.d.ts +9 -2
- package/dist/server/stateless.js +12 -21
- package/dist/shared/config.d.ts +33 -2
- package/dist/shared/config.js +89 -0
- package/package.json +7 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import type { z } from "zod";
|
|
2
4
|
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
5
|
import { type SessionStore } from "./session.js";
|
|
4
6
|
/**
|
|
@@ -12,18 +14,27 @@ export type CreateServerFn<T = Record<string, unknown>> = (arg: CreateServerArg<
|
|
|
12
14
|
/**
|
|
13
15
|
* Configuration options for the stateful server
|
|
14
16
|
*/
|
|
15
|
-
export interface StatefulServerOptions {
|
|
17
|
+
export interface StatefulServerOptions<T = Record<string, unknown>> {
|
|
16
18
|
/**
|
|
17
19
|
* Session store to use for managing active sessions
|
|
18
20
|
*/
|
|
19
21
|
sessionStore?: SessionStore<StreamableHTTPServerTransport>;
|
|
22
|
+
/**
|
|
23
|
+
* Zod schema for config validation
|
|
24
|
+
*/
|
|
25
|
+
schema?: z.ZodSchema<T>;
|
|
26
|
+
/**
|
|
27
|
+
* Express app instance to use (optional)
|
|
28
|
+
*/
|
|
29
|
+
app?: express.Application;
|
|
20
30
|
}
|
|
21
31
|
/**
|
|
22
32
|
* Creates a stateful server for handling MCP requests.
|
|
23
33
|
* For every new session, we invoke createMcpServer to create a new instance of the server.
|
|
24
34
|
* @param createMcpServer Function to create an MCP server
|
|
35
|
+
* @param options Configuration options including optional schema validation and Express app
|
|
25
36
|
* @returns Express app
|
|
26
37
|
*/
|
|
27
|
-
export declare function createStatefulServer<T = Record<string, unknown>>(createMcpServer: CreateServerFn<T>, options?: StatefulServerOptions): {
|
|
28
|
-
app:
|
|
38
|
+
export declare function createStatefulServer<T = Record<string, unknown>>(createMcpServer: CreateServerFn<T>, options?: StatefulServerOptions<T>): {
|
|
39
|
+
app: express.Application;
|
|
29
40
|
};
|
package/dist/server/stateful.js
CHANGED
|
@@ -2,16 +2,17 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
3
3
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import express from "express";
|
|
5
|
-
import {
|
|
5
|
+
import { parseAndValidateConfig } from "../shared/config.js";
|
|
6
6
|
import { createLRUStore } from "./session.js";
|
|
7
7
|
/**
|
|
8
8
|
* Creates a stateful server for handling MCP requests.
|
|
9
9
|
* For every new session, we invoke createMcpServer to create a new instance of the server.
|
|
10
10
|
* @param createMcpServer Function to create an MCP server
|
|
11
|
+
* @param options Configuration options including optional schema validation and Express app
|
|
11
12
|
* @returns Express app
|
|
12
13
|
*/
|
|
13
14
|
export function createStatefulServer(createMcpServer, options) {
|
|
14
|
-
const app = express();
|
|
15
|
+
const app = options?.app ?? express();
|
|
15
16
|
app.use(express.json());
|
|
16
17
|
const sessionStore = options?.sessionStore ?? createLRUStore();
|
|
17
18
|
// Handle POST requests for client-to-server communication
|
|
@@ -40,21 +41,14 @@ export function createStatefulServer(createMcpServer, options) {
|
|
|
40
41
|
sessionStore.delete?.(transport.sessionId);
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
res.status(400).json({
|
|
49
|
-
jsonrpc: "2.0",
|
|
50
|
-
error: {
|
|
51
|
-
code: -32000,
|
|
52
|
-
message: "Bad Request: Invalid configuration",
|
|
53
|
-
},
|
|
54
|
-
id: null,
|
|
55
|
-
});
|
|
44
|
+
// New session - validate config
|
|
45
|
+
const configResult = parseAndValidateConfig(req, options?.schema);
|
|
46
|
+
if (!configResult.ok) {
|
|
47
|
+
const status = configResult.error.status || 400;
|
|
48
|
+
res.status(status).json(configResult.error);
|
|
56
49
|
return;
|
|
57
50
|
}
|
|
51
|
+
const config = configResult.value;
|
|
58
52
|
try {
|
|
59
53
|
const server = createMcpServer({
|
|
60
54
|
sessionId: newSessionId,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import type { z } from "zod";
|
|
1
3
|
import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
4
|
/**
|
|
3
5
|
* Arguments when we create a new instance of your server
|
|
@@ -6,12 +8,17 @@ export interface CreateServerArg<T = Record<string, unknown>> {
|
|
|
6
8
|
config: T;
|
|
7
9
|
}
|
|
8
10
|
export type CreateServerFn<T = Record<string, unknown>> = (arg: CreateServerArg<T>) => Server;
|
|
11
|
+
export interface CreateStatelessServerOptions<T> {
|
|
12
|
+
schema?: z.ZodSchema<T>;
|
|
13
|
+
app?: express.Application;
|
|
14
|
+
}
|
|
9
15
|
/**
|
|
10
16
|
* Creates a stateless server for handling MCP requests
|
|
11
17
|
* In stateless mode, each request creates a new server and transport instance
|
|
12
18
|
* @param createMcpServer Function to create an MCP server
|
|
19
|
+
* @param options Optional configuration including Zod schema for validation
|
|
13
20
|
* @returns Express app
|
|
14
21
|
*/
|
|
15
|
-
export declare function createStatelessServer<T = Record<string, unknown>>(createMcpServer: CreateServerFn<T>): {
|
|
16
|
-
app:
|
|
22
|
+
export declare function createStatelessServer<T = Record<string, unknown>>(createMcpServer: CreateServerFn<T>, options?: CreateStatelessServerOptions<T>): {
|
|
23
|
+
app: express.Application;
|
|
17
24
|
};
|
package/dist/server/stateless.js
CHANGED
|
@@ -1,38 +1,29 @@
|
|
|
1
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
2
1
|
import express from "express";
|
|
3
|
-
import {
|
|
2
|
+
import { parseAndValidateConfig } from "../shared/config.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
4
|
/**
|
|
5
5
|
* Creates a stateless server for handling MCP requests
|
|
6
6
|
* In stateless mode, each request creates a new server and transport instance
|
|
7
7
|
* @param createMcpServer Function to create an MCP server
|
|
8
|
+
* @param options Optional configuration including Zod schema for validation
|
|
8
9
|
* @returns Express app
|
|
9
10
|
*/
|
|
10
|
-
export function createStatelessServer(createMcpServer) {
|
|
11
|
-
const app = express();
|
|
11
|
+
export function createStatelessServer(createMcpServer, options) {
|
|
12
|
+
const app = options?.app ?? express();
|
|
12
13
|
app.use(express.json());
|
|
13
14
|
app.post("/mcp", async (req, res) => {
|
|
14
15
|
// In stateless mode, create a new instance of transport and server for each request
|
|
15
16
|
// to ensure complete isolation. A single instance would cause request ID collisions
|
|
16
17
|
// when multiple clients connect concurrently.
|
|
17
18
|
try {
|
|
18
|
-
// Parse
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
catch (configError) {
|
|
25
|
-
res.status(400).json({
|
|
26
|
-
jsonrpc: "2.0",
|
|
27
|
-
error: {
|
|
28
|
-
code: -32000,
|
|
29
|
-
message: "Bad Request: Invalid configuration",
|
|
30
|
-
},
|
|
31
|
-
id: null,
|
|
32
|
-
});
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
19
|
+
// Parse and validate config
|
|
20
|
+
const configResult = parseAndValidateConfig(req, options?.schema);
|
|
21
|
+
if (!configResult.ok) {
|
|
22
|
+
const status = configResult.error.status;
|
|
23
|
+
res.status(status).json(configResult.error);
|
|
24
|
+
return;
|
|
35
25
|
}
|
|
26
|
+
const config = configResult.value;
|
|
36
27
|
// Create a new server instance with config
|
|
37
28
|
const server = createMcpServer({ config: config });
|
|
38
29
|
// Create a new transport instance
|
package/dist/shared/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { Request as ExpressRequest } from "express";
|
|
2
|
+
import type { z } from "zod";
|
|
2
3
|
export interface SmitheryUrlOptions {
|
|
3
4
|
apiKey?: string;
|
|
4
5
|
profile?: string;
|
|
@@ -16,4 +17,34 @@ export declare function createSmitheryUrl(baseUrl: string, options?: SmitheryUrl
|
|
|
16
17
|
* @param req The express request
|
|
17
18
|
* @returns The config
|
|
18
19
|
*/
|
|
19
|
-
export declare function parseExpressRequestConfig(req:
|
|
20
|
+
export declare function parseExpressRequestConfig(req: ExpressRequest): Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Parses and validates config from an Express request with optional Zod schema validation
|
|
23
|
+
* Supports both base64-encoded config and dot-notation config parameters
|
|
24
|
+
* @param req The express request
|
|
25
|
+
* @param schema Optional Zod schema for validation
|
|
26
|
+
* @returns Result with either parsed data or error response
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseAndValidateConfig<T = Record<string, unknown>>(req: ExpressRequest, schema?: z.ZodSchema<T>): import("okay-error").Err<{
|
|
29
|
+
title: string;
|
|
30
|
+
status: number;
|
|
31
|
+
detail: string;
|
|
32
|
+
instance: string;
|
|
33
|
+
}> | import("okay-error").Err<{
|
|
34
|
+
readonly title: "Invalid configuration parameters";
|
|
35
|
+
readonly status: 422;
|
|
36
|
+
readonly detail: "One or more config parameters are invalid.";
|
|
37
|
+
readonly instance: string;
|
|
38
|
+
readonly configSchema: import("zod-to-json-schema").JsonSchema7Type & {
|
|
39
|
+
$schema?: string | undefined;
|
|
40
|
+
definitions?: {
|
|
41
|
+
[key: string]: import("zod-to-json-schema").JsonSchema7Type;
|
|
42
|
+
} | undefined;
|
|
43
|
+
};
|
|
44
|
+
readonly errors: {
|
|
45
|
+
param: string;
|
|
46
|
+
pointer: string;
|
|
47
|
+
reason: string;
|
|
48
|
+
received: unknown;
|
|
49
|
+
}[];
|
|
50
|
+
}> | import("okay-error").Ok<T>;
|
package/dist/shared/config.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { err, ok } from "okay-error";
|
|
3
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
1
4
|
/**
|
|
2
5
|
* Creates a URL to connect to the Smithery MCP server.
|
|
3
6
|
* @param baseUrl The base URL of the Smithery server
|
|
@@ -28,3 +31,89 @@ export function createSmitheryUrl(baseUrl, options) {
|
|
|
28
31
|
export function parseExpressRequestConfig(req) {
|
|
29
32
|
return JSON.parse(Buffer.from(req.query.config, "base64").toString());
|
|
30
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Parses and validates config from an Express request with optional Zod schema validation
|
|
36
|
+
* Supports both base64-encoded config and dot-notation config parameters
|
|
37
|
+
* @param req The express request
|
|
38
|
+
* @param schema Optional Zod schema for validation
|
|
39
|
+
* @returns Result with either parsed data or error response
|
|
40
|
+
*/
|
|
41
|
+
export function parseAndValidateConfig(req, schema) {
|
|
42
|
+
// Parse config from request parameters
|
|
43
|
+
let config = {};
|
|
44
|
+
// 1. Process base64-encoded config parameter if present
|
|
45
|
+
if (req.query.config) {
|
|
46
|
+
try {
|
|
47
|
+
config = parseExpressRequestConfig(req);
|
|
48
|
+
}
|
|
49
|
+
catch (configError) {
|
|
50
|
+
return err({
|
|
51
|
+
title: "Invalid config parameter",
|
|
52
|
+
status: 400,
|
|
53
|
+
detail: "Failed to parse config parameter",
|
|
54
|
+
instance: req.originalUrl,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 2. Process dot-notation config parameters (foo=bar, a.b=c)
|
|
59
|
+
// This allows URL params like ?server.host=localhost&server.port=8080&debug=true
|
|
60
|
+
for (const [key, value] of Object.entries(req.query)) {
|
|
61
|
+
// Skip reserved parameters
|
|
62
|
+
if (key === "config" || key === "api_key" || key === "profile")
|
|
63
|
+
continue;
|
|
64
|
+
const pathParts = key.split(".");
|
|
65
|
+
// Handle array values from Express query parsing
|
|
66
|
+
const rawValue = Array.isArray(value) ? value[0] : value;
|
|
67
|
+
if (typeof rawValue !== "string")
|
|
68
|
+
continue;
|
|
69
|
+
// Try to parse value as JSON (for booleans, numbers, objects)
|
|
70
|
+
let parsedValue = rawValue;
|
|
71
|
+
try {
|
|
72
|
+
parsedValue = JSON.parse(rawValue);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// If parsing fails, use the raw string value
|
|
76
|
+
}
|
|
77
|
+
// Use lodash's set method to handle nested paths
|
|
78
|
+
_.set(config, pathParts, parsedValue);
|
|
79
|
+
}
|
|
80
|
+
// Validate config against schema if provided
|
|
81
|
+
if (schema) {
|
|
82
|
+
const result = schema.safeParse(config);
|
|
83
|
+
if (!result.success) {
|
|
84
|
+
const jsonSchema = zodToJsonSchema(schema, {
|
|
85
|
+
name: "ConfigSchema",
|
|
86
|
+
$refStrategy: "none",
|
|
87
|
+
});
|
|
88
|
+
const errors = result.error.issues.map((issue) => {
|
|
89
|
+
// Safely traverse the config object to get the received value
|
|
90
|
+
let received = config;
|
|
91
|
+
for (const key of issue.path) {
|
|
92
|
+
if (received && typeof received === "object" && key in received) {
|
|
93
|
+
received = received[key];
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
received = undefined;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
param: issue.path.join(".") || "root",
|
|
102
|
+
pointer: `/${issue.path.join("/")}`,
|
|
103
|
+
reason: issue.message,
|
|
104
|
+
received,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
return err({
|
|
108
|
+
title: "Invalid configuration parameters",
|
|
109
|
+
status: 422,
|
|
110
|
+
detail: "One or more config parameters are invalid.",
|
|
111
|
+
instance: req.originalUrl,
|
|
112
|
+
configSchema: jsonSchema,
|
|
113
|
+
errors,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return ok(result.data);
|
|
117
|
+
}
|
|
118
|
+
return ok(config);
|
|
119
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smithery/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "SDK to develop with Smithery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,12 +25,17 @@
|
|
|
25
25
|
"ai": "^4.3.15",
|
|
26
26
|
"express": "^5.1.0",
|
|
27
27
|
"json-schema": "^0.4.0",
|
|
28
|
+
"lodash": "^4.17.21",
|
|
29
|
+
"okay-error": "^1.0.2",
|
|
28
30
|
"openai": "^4.0.0",
|
|
29
|
-
"uuid": "^11.0.3"
|
|
31
|
+
"uuid": "^11.0.3",
|
|
32
|
+
"zod": "^3.23.8",
|
|
33
|
+
"zod-to-json-schema": "^3.24.1"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
36
|
"@types/express": "^5.0.1",
|
|
33
37
|
"@types/json-schema": "^7.0.15",
|
|
38
|
+
"@types/lodash": "^4.17.17",
|
|
34
39
|
"@types/node": "^20.0.0",
|
|
35
40
|
"@types/uuid": "^9.0.7",
|
|
36
41
|
"dotenv": "^16.4.7",
|