@prmichaelsen/mcp-auth 0.1.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/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/auth/base-provider.js +173 -0
- package/dist/auth/base-provider.js.map +7 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +7 -0
- package/dist/auth/providers/env-provider.js +71 -0
- package/dist/auth/providers/env-provider.js.map +7 -0
- package/dist/auth/providers/index.js +11 -0
- package/dist/auth/providers/index.js.map +7 -0
- package/dist/auth/providers/simple-resolver.js +185 -0
- package/dist/auth/providers/simple-resolver.js.map +7 -0
- package/dist/auth/types.js +1 -0
- package/dist/auth/types.js.map +7 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +7 -0
- package/dist/server/config.js +1 -0
- package/dist/server/config.js.map +7 -0
- package/dist/server/decorators.js +149 -0
- package/dist/server/decorators.js.map +7 -0
- package/dist/server/index.js +25 -0
- package/dist/server/index.js.map +7 -0
- package/dist/server/mcp-server.js +269 -0
- package/dist/server/mcp-server.js.map +7 -0
- package/dist/server/tool.js +66 -0
- package/dist/server/tool.js.map +7 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +7 -0
- package/dist/utils/errors.js +143 -0
- package/dist/utils/errors.js.map +7 -0
- package/dist/utils/index.js +81 -0
- package/dist/utils/index.js.map +7 -0
- package/dist/utils/logger.js +200 -0
- package/dist/utils/logger.js.map +7 -0
- package/dist/utils/validation.js +172 -0
- package/dist/utils/validation.js.map +7 -0
- package/dist/wrapper/config.js +1 -0
- package/dist/wrapper/config.js.map +7 -0
- package/dist/wrapper/index.js +10 -0
- package/dist/wrapper/index.js.map +7 -0
- package/dist/wrapper/server-wrapper.js +427 -0
- package/dist/wrapper/server-wrapper.js.map +7 -0
- package/package.json +93 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import {
|
|
4
|
+
ListToolsRequestSchema,
|
|
5
|
+
CallToolRequestSchema
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { AuthenticatedTool } from "./tool.js";
|
|
8
|
+
import { ConfigurationError, TransportError } from "../utils/errors.js";
|
|
9
|
+
import { createLogger } from "../utils/logger.js";
|
|
10
|
+
import { validateResourceType } from "../utils/validation.js";
|
|
11
|
+
class AuthenticatedMCPServer {
|
|
12
|
+
config;
|
|
13
|
+
mcpServer;
|
|
14
|
+
logger;
|
|
15
|
+
tools;
|
|
16
|
+
isRunning = false;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.validateConfig(config);
|
|
19
|
+
this.config = this.normalizeConfig(config);
|
|
20
|
+
this.logger = createLogger(this.config.middleware.logging);
|
|
21
|
+
this.tools = /* @__PURE__ */ new Map();
|
|
22
|
+
this.mcpServer = new Server(
|
|
23
|
+
{
|
|
24
|
+
name: this.config.name,
|
|
25
|
+
version: this.config.version
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
capabilities: {
|
|
29
|
+
tools: {}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
this.registerMCPHandlers();
|
|
34
|
+
this.logger.info("AuthenticatedMCPServer created", {
|
|
35
|
+
name: this.config.name,
|
|
36
|
+
version: this.config.version,
|
|
37
|
+
resourceType: this.config.resourceType,
|
|
38
|
+
transport: this.config.transport.type
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validate server configuration
|
|
43
|
+
*/
|
|
44
|
+
validateConfig(config) {
|
|
45
|
+
if (!config.authProvider) {
|
|
46
|
+
throw new ConfigurationError("authProvider is required");
|
|
47
|
+
}
|
|
48
|
+
if (!config.tokenResolver) {
|
|
49
|
+
throw new ConfigurationError("tokenResolver is required");
|
|
50
|
+
}
|
|
51
|
+
if (!config.resourceType) {
|
|
52
|
+
throw new ConfigurationError("resourceType is required");
|
|
53
|
+
}
|
|
54
|
+
if (!config.transport) {
|
|
55
|
+
throw new ConfigurationError("transport is required");
|
|
56
|
+
}
|
|
57
|
+
validateResourceType(config.resourceType);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Normalize configuration with defaults
|
|
61
|
+
*/
|
|
62
|
+
normalizeConfig(config) {
|
|
63
|
+
return {
|
|
64
|
+
name: config.name ?? "mcp-server",
|
|
65
|
+
version: config.version ?? "1.0.0",
|
|
66
|
+
authProvider: config.authProvider,
|
|
67
|
+
tokenResolver: config.tokenResolver,
|
|
68
|
+
resourceType: config.resourceType,
|
|
69
|
+
transport: config.transport,
|
|
70
|
+
middleware: {
|
|
71
|
+
rateLimit: config.middleware?.rateLimit,
|
|
72
|
+
logging: config.middleware?.logging ?? { enabled: true, level: "info" }
|
|
73
|
+
},
|
|
74
|
+
requestTimeoutMs: config.requestTimeoutMs ?? 3e4,
|
|
75
|
+
enableTracing: config.enableTracing ?? false,
|
|
76
|
+
errorHandler: config.errorHandler
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Register MCP protocol handlers
|
|
81
|
+
*/
|
|
82
|
+
registerMCPHandlers() {
|
|
83
|
+
this.mcpServer.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
84
|
+
const tools = Array.from(this.tools.values()).map((tool) => ({
|
|
85
|
+
name: tool.name,
|
|
86
|
+
description: tool.description,
|
|
87
|
+
inputSchema: tool.inputSchema
|
|
88
|
+
}));
|
|
89
|
+
this.logger.debug("Listing tools", { count: tools.length });
|
|
90
|
+
return { tools };
|
|
91
|
+
});
|
|
92
|
+
this.mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
93
|
+
const { name, arguments: args } = request.params;
|
|
94
|
+
this.logger.info("Tool called", { toolName: name });
|
|
95
|
+
const tool = this.tools.get(name);
|
|
96
|
+
if (!tool) {
|
|
97
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
98
|
+
}
|
|
99
|
+
const context = {
|
|
100
|
+
transport: this.config.transport.type,
|
|
101
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
102
|
+
metadata: { toolName: name }
|
|
103
|
+
};
|
|
104
|
+
const result = await tool.handler(
|
|
105
|
+
args,
|
|
106
|
+
context,
|
|
107
|
+
this.config.authProvider,
|
|
108
|
+
this.config.tokenResolver,
|
|
109
|
+
this.config.resourceType
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Register a tool with authentication
|
|
123
|
+
*
|
|
124
|
+
* @param name - Tool name
|
|
125
|
+
* @param handler - Authenticated tool handler
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {
|
|
130
|
+
* return getData(args, accessToken);
|
|
131
|
+
* }));
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
registerTool(name, handler, options) {
|
|
135
|
+
if (this.tools.has(name)) {
|
|
136
|
+
throw new ConfigurationError(`Tool '${name}' is already registered`);
|
|
137
|
+
}
|
|
138
|
+
this.tools.set(name, {
|
|
139
|
+
name,
|
|
140
|
+
description: options?.description,
|
|
141
|
+
inputSchema: options?.inputSchema,
|
|
142
|
+
handler
|
|
143
|
+
});
|
|
144
|
+
this.logger.debug("Tool registered", { name });
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Register a Tool class instance
|
|
148
|
+
*
|
|
149
|
+
* @param tool - Tool or AuthenticatedTool instance
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* server.registerToolClass(new AuthenticatedTool(new GetProfileTool()));
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
registerToolClass(tool) {
|
|
157
|
+
const authenticatedTool = tool instanceof AuthenticatedTool ? tool : new AuthenticatedTool(tool);
|
|
158
|
+
const handler = async (args, context, authProvider, tokenResolver, resourceType) => {
|
|
159
|
+
return authenticatedTool.execute(args, context, authProvider, tokenResolver, resourceType);
|
|
160
|
+
};
|
|
161
|
+
this.registerTool(authenticatedTool.name, handler, {
|
|
162
|
+
description: authenticatedTool.description,
|
|
163
|
+
inputSchema: authenticatedTool.inputSchema
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Register multiple tools at once
|
|
168
|
+
*
|
|
169
|
+
* @param tools - Array of Tool or AuthenticatedTool instances
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* server.registerTools([
|
|
174
|
+
* new AuthenticatedTool(new GetProfileTool()),
|
|
175
|
+
* new AuthenticatedTool(new GetMediaTool())
|
|
176
|
+
* ]);
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
registerTools(tools) {
|
|
180
|
+
for (const tool of tools) {
|
|
181
|
+
this.registerToolClass(tool);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Start the server
|
|
186
|
+
*/
|
|
187
|
+
async start() {
|
|
188
|
+
if (this.isRunning) {
|
|
189
|
+
throw new ConfigurationError("Server is already running");
|
|
190
|
+
}
|
|
191
|
+
this.logger.info("Starting authenticated MCP server");
|
|
192
|
+
if (this.config.authProvider.initialize) {
|
|
193
|
+
await this.config.authProvider.initialize();
|
|
194
|
+
this.logger.debug("Auth provider initialized");
|
|
195
|
+
}
|
|
196
|
+
if (this.config.tokenResolver.initialize) {
|
|
197
|
+
await this.config.tokenResolver.initialize();
|
|
198
|
+
this.logger.debug("Token resolver initialized");
|
|
199
|
+
}
|
|
200
|
+
switch (this.config.transport.type) {
|
|
201
|
+
case "stdio":
|
|
202
|
+
await this.startStdioTransport();
|
|
203
|
+
break;
|
|
204
|
+
case "sse":
|
|
205
|
+
case "http":
|
|
206
|
+
throw new TransportError(
|
|
207
|
+
"SSE/HTTP transports not yet implemented for tool-level auth. Use server wrapping pattern for remote transports."
|
|
208
|
+
);
|
|
209
|
+
default:
|
|
210
|
+
throw new TransportError(`Unsupported transport: ${this.config.transport.type}`);
|
|
211
|
+
}
|
|
212
|
+
this.isRunning = true;
|
|
213
|
+
this.logger.info("Server started successfully", {
|
|
214
|
+
name: this.config.name,
|
|
215
|
+
transport: this.config.transport.type,
|
|
216
|
+
toolCount: this.tools.size
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Stop the server
|
|
221
|
+
*/
|
|
222
|
+
async stop() {
|
|
223
|
+
if (!this.isRunning) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.logger.info("Stopping server");
|
|
227
|
+
await this.mcpServer.close();
|
|
228
|
+
if (this.config.authProvider.cleanup) {
|
|
229
|
+
await this.config.authProvider.cleanup();
|
|
230
|
+
}
|
|
231
|
+
if (this.config.tokenResolver.cleanup) {
|
|
232
|
+
await this.config.tokenResolver.cleanup();
|
|
233
|
+
}
|
|
234
|
+
this.isRunning = false;
|
|
235
|
+
this.logger.info("Server stopped");
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Start stdio transport
|
|
239
|
+
*/
|
|
240
|
+
async startStdioTransport() {
|
|
241
|
+
this.logger.info("Starting stdio transport");
|
|
242
|
+
const transport = new StdioServerTransport();
|
|
243
|
+
await this.mcpServer.connect(transport);
|
|
244
|
+
this.logger.info("Stdio transport connected");
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get server statistics
|
|
248
|
+
*/
|
|
249
|
+
getStats() {
|
|
250
|
+
return {
|
|
251
|
+
name: this.config.name,
|
|
252
|
+
version: this.config.version,
|
|
253
|
+
resourceType: this.config.resourceType,
|
|
254
|
+
transport: this.config.transport.type,
|
|
255
|
+
toolCount: this.tools.size,
|
|
256
|
+
isRunning: this.isRunning
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get list of registered tool names
|
|
261
|
+
*/
|
|
262
|
+
getToolNames() {
|
|
263
|
+
return Array.from(this.tools.keys());
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export {
|
|
267
|
+
AuthenticatedMCPServer
|
|
268
|
+
};
|
|
269
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/server/mcp-server.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Authenticated MCP Server implementation\n * \n * MCP server with integrated authentication for tool-level auth pattern.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n ListToolsRequestSchema,\n CallToolRequestSchema,\n type CallToolRequest\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { ServerConfig, NormalizedServerConfig } from './config.js';\nimport type { RequestContext, ToolHandler } from '../types.js';\nimport type { AuthenticatedToolHandler } from './decorators.js';\nimport { AuthenticatedTool, type Tool } from './tool.js';\nimport { ConfigurationError, TransportError } from '../utils/errors.js';\nimport { createLogger, type Logger } from '../utils/logger.js';\nimport { validateResourceType } from '../utils/validation.js';\n\n/**\n * Tool registration entry\n */\ninterface ToolRegistration {\n name: string;\n description?: string;\n inputSchema?: object;\n handler: AuthenticatedToolHandler;\n}\n\n/**\n * Authenticated MCP Server\n * \n * MCP server with integrated authentication support.\n * Use this for building new MCP servers with tool-level authentication.\n * \n * @example\n * ```typescript\n * const server = new AuthenticatedMCPServer({\n * name: 'my-server',\n * authProvider: new EnvAuthProvider(),\n * tokenResolver: new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' }),\n * resourceType: 'myapi',\n * transport: { type: 'stdio' }\n * });\n * \n * server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {\n * const client = new APIClient(accessToken);\n * return client.getData(args);\n * }));\n * \n * await server.start();\n * ```\n */\nexport class AuthenticatedMCPServer {\n private config: NormalizedServerConfig;\n private mcpServer: Server;\n private logger: Logger;\n private tools: Map<string, ToolRegistration>;\n private isRunning: boolean = false;\n \n constructor(config: ServerConfig) {\n // Validate configuration\n this.validateConfig(config);\n \n // Normalize configuration\n this.config = this.normalizeConfig(config);\n \n // Initialize logger\n this.logger = createLogger(this.config.middleware.logging);\n \n // Initialize tools map\n this.tools = new Map();\n \n // Create MCP server\n this.mcpServer = new Server(\n {\n name: this.config.name,\n version: this.config.version\n },\n {\n capabilities: {\n tools: {}\n }\n }\n );\n \n // Register MCP handlers\n this.registerMCPHandlers();\n \n this.logger.info('AuthenticatedMCPServer created', {\n name: this.config.name,\n version: this.config.version,\n resourceType: this.config.resourceType,\n transport: this.config.transport.type\n });\n }\n \n /**\n * Validate server configuration\n */\n private validateConfig(config: ServerConfig): void {\n if (!config.authProvider) {\n throw new ConfigurationError('authProvider is required');\n }\n if (!config.tokenResolver) {\n throw new ConfigurationError('tokenResolver is required');\n }\n if (!config.resourceType) {\n throw new ConfigurationError('resourceType is required');\n }\n if (!config.transport) {\n throw new ConfigurationError('transport is required');\n }\n \n validateResourceType(config.resourceType);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: ServerConfig): NormalizedServerConfig {\n return {\n name: config.name ?? 'mcp-server',\n version: config.version ?? '1.0.0',\n authProvider: config.authProvider,\n tokenResolver: config.tokenResolver,\n resourceType: config.resourceType,\n transport: config.transport,\n middleware: {\n rateLimit: config.middleware?.rateLimit,\n logging: config.middleware?.logging ?? { enabled: true, level: 'info' }\n },\n requestTimeoutMs: config.requestTimeoutMs ?? 30000,\n enableTracing: config.enableTracing ?? false,\n errorHandler: config.errorHandler\n };\n }\n \n /**\n * Register MCP protocol handlers\n */\n private registerMCPHandlers(): void {\n // List tools handler\n this.mcpServer.setRequestHandler(ListToolsRequestSchema, async () => {\n const tools = Array.from(this.tools.values()).map(tool => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema\n }));\n \n this.logger.debug('Listing tools', { count: tools.length });\n \n return { tools };\n });\n \n // Call tool handler\n this.mcpServer.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const { name, arguments: args } = request.params;\n \n this.logger.info('Tool called', { toolName: name });\n \n const tool = this.tools.get(name);\n \n if (!tool) {\n throw new Error(`Unknown tool: ${name}`);\n }\n \n // Create request context\n // Note: For stdio, we don't have headers, so context is minimal\n const context: RequestContext = {\n transport: this.config.transport.type,\n timestamp: new Date(),\n metadata: { toolName: name }\n };\n \n // Execute tool with authentication\n const result = await tool.handler(\n args,\n context,\n this.config.authProvider,\n this.config.tokenResolver,\n this.config.resourceType\n );\n \n // Return MCP-formatted response\n return {\n content: [\n {\n type: 'text',\n text: typeof result === 'string' ? result : JSON.stringify(result, null, 2)\n }\n ]\n };\n });\n }\n \n /**\n * Register a tool with authentication\n * \n * @param name - Tool name\n * @param handler - Authenticated tool handler\n * \n * @example\n * ```typescript\n * server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {\n * return getData(args, accessToken);\n * }));\n * ```\n */\n registerTool<TArgs = any, TResult = any>(\n name: string,\n handler: AuthenticatedToolHandler<TArgs, TResult>,\n options?: {\n description?: string;\n inputSchema?: object;\n }\n ): void {\n if (this.tools.has(name)) {\n throw new ConfigurationError(`Tool '${name}' is already registered`);\n }\n \n this.tools.set(name, {\n name,\n description: options?.description,\n inputSchema: options?.inputSchema,\n handler\n });\n \n this.logger.debug('Tool registered', { name });\n }\n \n /**\n * Register a Tool class instance\n * \n * @param tool - Tool or AuthenticatedTool instance\n * \n * @example\n * ```typescript\n * server.registerToolClass(new AuthenticatedTool(new GetProfileTool()));\n * ```\n */\n registerToolClass(tool: Tool | AuthenticatedTool): void {\n const authenticatedTool = tool instanceof AuthenticatedTool \n ? tool \n : new AuthenticatedTool(tool);\n \n const handler: AuthenticatedToolHandler = async (args, context, authProvider, tokenResolver, resourceType) => {\n return authenticatedTool.execute(args, context, authProvider, tokenResolver, resourceType);\n };\n \n this.registerTool(authenticatedTool.name, handler, {\n description: authenticatedTool.description,\n inputSchema: authenticatedTool.inputSchema\n });\n }\n \n /**\n * Register multiple tools at once\n * \n * @param tools - Array of Tool or AuthenticatedTool instances\n * \n * @example\n * ```typescript\n * server.registerTools([\n * new AuthenticatedTool(new GetProfileTool()),\n * new AuthenticatedTool(new GetMediaTool())\n * ]);\n * ```\n */\n registerTools(tools: Array<Tool | AuthenticatedTool>): void {\n for (const tool of tools) {\n this.registerToolClass(tool);\n }\n }\n \n /**\n * Start the server\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new ConfigurationError('Server is already running');\n }\n \n this.logger.info('Starting authenticated MCP server');\n \n // Initialize auth provider\n if (this.config.authProvider.initialize) {\n await this.config.authProvider.initialize();\n this.logger.debug('Auth provider initialized');\n }\n \n // Initialize token resolver\n if (this.config.tokenResolver.initialize) {\n await this.config.tokenResolver.initialize();\n this.logger.debug('Token resolver initialized');\n }\n \n // Start transport\n switch (this.config.transport.type) {\n case 'stdio':\n await this.startStdioTransport();\n break;\n case 'sse':\n case 'http':\n throw new TransportError(\n 'SSE/HTTP transports not yet implemented for tool-level auth. ' +\n 'Use server wrapping pattern for remote transports.'\n );\n default:\n throw new TransportError(`Unsupported transport: ${this.config.transport.type}`);\n }\n \n this.isRunning = true;\n \n this.logger.info('Server started successfully', {\n name: this.config.name,\n transport: this.config.transport.type,\n toolCount: this.tools.size\n });\n }\n \n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n \n this.logger.info('Stopping server');\n \n // Close MCP server\n await this.mcpServer.close();\n \n // Cleanup auth provider\n if (this.config.authProvider.cleanup) {\n await this.config.authProvider.cleanup();\n }\n \n // Cleanup token resolver\n if (this.config.tokenResolver.cleanup) {\n await this.config.tokenResolver.cleanup();\n }\n \n this.isRunning = false;\n \n this.logger.info('Server stopped');\n }\n \n /**\n * Start stdio transport\n */\n private async startStdioTransport(): Promise<void> {\n this.logger.info('Starting stdio transport');\n \n const transport = new StdioServerTransport();\n await this.mcpServer.connect(transport);\n \n this.logger.info('Stdio transport connected');\n }\n \n /**\n * Get server statistics\n */\n getStats(): {\n name: string;\n version: string;\n resourceType: string;\n transport: string;\n toolCount: number;\n isRunning: boolean;\n } {\n return {\n name: this.config.name,\n version: this.config.version,\n resourceType: this.config.resourceType,\n transport: this.config.transport.type,\n toolCount: this.tools.size,\n isRunning: this.isRunning\n };\n }\n \n /**\n * Get list of registered tool names\n */\n getToolNames(): string[] {\n return Array.from(this.tools.keys());\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AuthenticationError, TokenResolutionError } from "../utils/errors.js";
|
|
2
|
+
import { validateUserId, validateAccessToken } from "../utils/validation.js";
|
|
3
|
+
class AuthenticatedTool {
|
|
4
|
+
constructor(tool, options) {
|
|
5
|
+
this.tool = tool;
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Get tool name
|
|
10
|
+
*/
|
|
11
|
+
get name() {
|
|
12
|
+
return this.tool.name;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get tool description
|
|
16
|
+
*/
|
|
17
|
+
get description() {
|
|
18
|
+
return this.tool.description;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get tool input schema
|
|
22
|
+
*/
|
|
23
|
+
get inputSchema() {
|
|
24
|
+
return this.tool.inputSchema;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Execute tool with authentication
|
|
28
|
+
*
|
|
29
|
+
* @param args - Tool arguments
|
|
30
|
+
* @param context - Request context
|
|
31
|
+
* @param authProvider - Authentication provider
|
|
32
|
+
* @param tokenResolver - Token resolver
|
|
33
|
+
* @param resourceType - Resource type
|
|
34
|
+
* @returns Tool result
|
|
35
|
+
*/
|
|
36
|
+
async execute(args, context, authProvider, tokenResolver, resourceType) {
|
|
37
|
+
const requiresAuth = this.options?.requiresAuth ?? true;
|
|
38
|
+
if (!requiresAuth) {
|
|
39
|
+
return this.tool.execute(args, "", "");
|
|
40
|
+
}
|
|
41
|
+
const authResult = await authProvider.authenticate(context);
|
|
42
|
+
if (!authResult.authenticated || !authResult.userId) {
|
|
43
|
+
throw new AuthenticationError(
|
|
44
|
+
authResult.error || "Authentication failed",
|
|
45
|
+
{ tool: this.tool.name }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const userId = validateUserId(authResult.userId);
|
|
49
|
+
const accessToken = await tokenResolver.resolveToken(userId, resourceType);
|
|
50
|
+
if (!accessToken) {
|
|
51
|
+
throw new TokenResolutionError(userId, resourceType, {
|
|
52
|
+
tool: this.tool.name
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
validateAccessToken(accessToken);
|
|
56
|
+
return this.tool.execute(args, accessToken, userId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function createAuthenticatedTool(tool, options) {
|
|
60
|
+
return new AuthenticatedTool(tool, options);
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
AuthenticatedTool,
|
|
64
|
+
createAuthenticatedTool
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/server/tool.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Tool interface and wrapper for tool-level authentication\n * \n * Provides class-based tool definitions with authentication support.\n */\n\nimport type { RequestContext } from '../types.js';\nimport type { AuthProvider, ResourceTokenResolver } from '../auth/types.js';\nimport { AuthenticationError, TokenResolutionError } from '../utils/errors.js';\nimport { validateUserId, validateAccessToken } from '../utils/validation.js';\n\n/**\n * Tool interface for class-based tools\n * \n * Implement this interface to create structured, reusable tools.\n * \n * @example\n * ```typescript\n * class GetProfileTool implements Tool {\n * name = 'get_profile';\n * description = 'Get user profile';\n * \n * inputSchema = {\n * type: 'object',\n * properties: {\n * userId: { type: 'string' }\n * }\n * };\n * \n * async execute(args: { userId: string }, accessToken: string, userId: string) {\n * const client = new APIClient(accessToken);\n * return client.getProfile(args.userId);\n * }\n * }\n * ```\n */\nexport interface Tool<TArgs = any, TResult = any> {\n /**\n * Tool name (must be unique within server)\n */\n name: string;\n \n /**\n * Tool description for clients\n */\n description?: string;\n \n /**\n * JSON Schema for tool input validation\n */\n inputSchema?: object;\n \n /**\n * Execute the tool with authenticated context\n * \n * @param args - Tool arguments\n * @param accessToken - Resource-specific access token\n * @param userId - Authenticated user ID\n * @returns Tool result\n */\n execute(args: TArgs, accessToken: string, userId: string): Promise<TResult>;\n}\n\n/**\n * Authenticated tool wrapper\n * \n * Wraps a Tool implementation with automatic authentication.\n * Handles auth provider and token resolver calls before executing the tool.\n * \n * @example\n * ```typescript\n * const tool = new AuthenticatedTool(new GetProfileTool());\n * server.registerTool(tool);\n * ```\n */\nexport class AuthenticatedTool<TArgs = any, TResult = any> {\n constructor(\n private tool: Tool<TArgs, TResult>,\n private options?: {\n /**\n * Whether authentication is required\n * @default true\n */\n requiresAuth?: boolean;\n }\n ) {}\n \n /**\n * Get tool name\n */\n get name(): string {\n return this.tool.name;\n }\n \n /**\n * Get tool description\n */\n get description(): string | undefined {\n return this.tool.description;\n }\n \n /**\n * Get tool input schema\n */\n get inputSchema(): object | undefined {\n return this.tool.inputSchema;\n }\n \n /**\n * Execute tool with authentication\n * \n * @param args - Tool arguments\n * @param context - Request context\n * @param authProvider - Authentication provider\n * @param tokenResolver - Token resolver\n * @param resourceType - Resource type\n * @returns Tool result\n */\n async execute(\n args: TArgs,\n context: RequestContext,\n authProvider: AuthProvider,\n tokenResolver: ResourceTokenResolver,\n resourceType: string\n ): Promise<TResult> {\n // Check if auth is required\n const requiresAuth = this.options?.requiresAuth ?? true;\n \n if (!requiresAuth) {\n // Execute without auth (use empty values)\n return this.tool.execute(args, '', '');\n }\n \n // 1. Authenticate request\n const authResult = await authProvider.authenticate(context);\n \n if (!authResult.authenticated || !authResult.userId) {\n throw new AuthenticationError(\n authResult.error || 'Authentication failed',\n { tool: this.tool.name }\n );\n }\n \n const userId = validateUserId(authResult.userId);\n \n // 2. Resolve resource token\n const accessToken = await tokenResolver.resolveToken(userId, resourceType);\n \n if (!accessToken) {\n throw new TokenResolutionError(userId, resourceType, {\n tool: this.tool.name\n });\n }\n \n validateAccessToken(accessToken);\n \n // 3. Execute tool\n return this.tool.execute(args, accessToken, userId);\n }\n}\n\n/**\n * Create an authenticated tool from a Tool implementation\n * \n * Helper function for creating authenticated tools.\n * \n * @param tool - Tool implementation\n * @param options - Authentication options\n * @returns Authenticated tool wrapper\n * \n * @example\n * ```typescript\n * const tool = createAuthenticatedTool(new GetProfileTool());\n * server.registerTool(tool);\n * ```\n */\nexport function createAuthenticatedTool<TArgs = any, TResult = any>(\n tool: Tool<TArgs, TResult>,\n options?: { requiresAuth?: boolean }\n): AuthenticatedTool<TArgs, TResult> {\n return new AuthenticatedTool(tool, options);\n}\n"],
|
|
5
|
+
"mappings": "AAQA,SAAS,qBAAqB,4BAA4B;AAC1D,SAAS,gBAAgB,2BAA2B;AAkE7C,MAAM,kBAA8C;AAAA,EACzD,YACU,MACA,SAOR;AARQ;AACA;AAAA,EAOP;AAAA;AAAA;AAAA;AAAA,EAKH,IAAI,OAAe;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,cAAkC;AACpC,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,cAAkC;AACpC,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,QACJ,MACA,SACA,cACA,eACA,cACkB;AAElB,UAAM,eAAe,KAAK,SAAS,gBAAgB;AAEnD,QAAI,CAAC,cAAc;AAEjB,aAAO,KAAK,KAAK,QAAQ,MAAM,IAAI,EAAE;AAAA,IACvC;AAGA,UAAM,aAAa,MAAM,aAAa,aAAa,OAAO;AAE1D,QAAI,CAAC,WAAW,iBAAiB,CAAC,WAAW,QAAQ;AACnD,YAAM,IAAI;AAAA,QACR,WAAW,SAAS;AAAA,QACpB,EAAE,MAAM,KAAK,KAAK,KAAK;AAAA,MACzB;AAAA,IACF;AAEA,UAAM,SAAS,eAAe,WAAW,MAAM;AAG/C,UAAM,cAAc,MAAM,cAAc,aAAa,QAAQ,YAAY;AAEzE,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,qBAAqB,QAAQ,cAAc;AAAA,QACnD,MAAM,KAAK,KAAK;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,wBAAoB,WAAW;AAG/B,WAAO,KAAK,KAAK,QAAQ,MAAM,aAAa,MAAM;AAAA,EACpD;AACF;AAiBO,SAAS,wBACd,MACA,SACmC;AACnC,SAAO,IAAI,kBAAkB,MAAM,OAAO;AAC5C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
class MCPAuthError extends Error {
|
|
2
|
+
/**
|
|
3
|
+
* Error code for programmatic handling
|
|
4
|
+
*/
|
|
5
|
+
code;
|
|
6
|
+
/**
|
|
7
|
+
* HTTP status code (for HTTP/SSE transports)
|
|
8
|
+
*/
|
|
9
|
+
statusCode;
|
|
10
|
+
/**
|
|
11
|
+
* Additional error details
|
|
12
|
+
*/
|
|
13
|
+
details;
|
|
14
|
+
constructor(message, code = "MCP_AUTH_ERROR", statusCode = 500, details) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = this.constructor.name;
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
|
+
this.details = details;
|
|
20
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
21
|
+
Error.captureStackTrace(this, this.constructor);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Convert error to JSON for logging/transmission
|
|
26
|
+
*/
|
|
27
|
+
toJSON() {
|
|
28
|
+
return {
|
|
29
|
+
name: this.name,
|
|
30
|
+
message: this.message,
|
|
31
|
+
code: this.code,
|
|
32
|
+
statusCode: this.statusCode,
|
|
33
|
+
details: this.details,
|
|
34
|
+
stack: this.stack
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
class AuthenticationError extends MCPAuthError {
|
|
39
|
+
constructor(message = "Authentication failed", details) {
|
|
40
|
+
super(message, "AUTHENTICATION_FAILED", 401, details);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
class TokenResolutionError extends MCPAuthError {
|
|
44
|
+
constructor(userId, resourceType, details) {
|
|
45
|
+
super(
|
|
46
|
+
`Failed to resolve ${resourceType} token for user ${userId}`,
|
|
47
|
+
"TOKEN_RESOLUTION_FAILED",
|
|
48
|
+
401,
|
|
49
|
+
{ userId, resourceType, ...details }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
class InvalidTokenError extends MCPAuthError {
|
|
54
|
+
constructor(message = "Invalid or expired token", details) {
|
|
55
|
+
super(message, "INVALID_TOKEN", 401, details);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
class MissingCredentialsError extends MCPAuthError {
|
|
59
|
+
constructor(message = "Missing authentication credentials", details) {
|
|
60
|
+
super(message, "MISSING_CREDENTIALS", 401, details);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
class ConfigurationError extends MCPAuthError {
|
|
64
|
+
constructor(message, details) {
|
|
65
|
+
super(message, "CONFIGURATION_ERROR", 500, details);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class RateLimitError extends MCPAuthError {
|
|
69
|
+
constructor(message = "Rate limit exceeded", retryAfter, details) {
|
|
70
|
+
super(
|
|
71
|
+
message,
|
|
72
|
+
"RATE_LIMIT_EXCEEDED",
|
|
73
|
+
429,
|
|
74
|
+
{ retryAfter, ...details }
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
class ServerPoolError extends MCPAuthError {
|
|
79
|
+
constructor(message, details) {
|
|
80
|
+
super(message, "SERVER_POOL_ERROR", 500, details);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class TransportError extends MCPAuthError {
|
|
84
|
+
constructor(message, details) {
|
|
85
|
+
super(message, "TRANSPORT_ERROR", 500, details);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
class ValidationError extends MCPAuthError {
|
|
89
|
+
constructor(message, details) {
|
|
90
|
+
super(message, "VALIDATION_ERROR", 400, details);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function isMCPAuthError(error) {
|
|
94
|
+
return error instanceof MCPAuthError;
|
|
95
|
+
}
|
|
96
|
+
function isAuthenticationError(error) {
|
|
97
|
+
return error instanceof AuthenticationError;
|
|
98
|
+
}
|
|
99
|
+
function isTokenResolutionError(error) {
|
|
100
|
+
return error instanceof TokenResolutionError;
|
|
101
|
+
}
|
|
102
|
+
function isRateLimitError(error) {
|
|
103
|
+
return error instanceof RateLimitError;
|
|
104
|
+
}
|
|
105
|
+
function formatErrorForClient(error) {
|
|
106
|
+
if (isMCPAuthError(error)) {
|
|
107
|
+
return {
|
|
108
|
+
error: error.message,
|
|
109
|
+
code: error.code,
|
|
110
|
+
statusCode: error.statusCode
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (error instanceof Error) {
|
|
114
|
+
return {
|
|
115
|
+
error: error.message,
|
|
116
|
+
code: "INTERNAL_ERROR",
|
|
117
|
+
statusCode: 500
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
error: "An unknown error occurred",
|
|
122
|
+
code: "UNKNOWN_ERROR",
|
|
123
|
+
statusCode: 500
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
export {
|
|
127
|
+
AuthenticationError,
|
|
128
|
+
ConfigurationError,
|
|
129
|
+
InvalidTokenError,
|
|
130
|
+
MCPAuthError,
|
|
131
|
+
MissingCredentialsError,
|
|
132
|
+
RateLimitError,
|
|
133
|
+
ServerPoolError,
|
|
134
|
+
TokenResolutionError,
|
|
135
|
+
TransportError,
|
|
136
|
+
ValidationError,
|
|
137
|
+
formatErrorForClient,
|
|
138
|
+
isAuthenticationError,
|
|
139
|
+
isMCPAuthError,
|
|
140
|
+
isRateLimitError,
|
|
141
|
+
isTokenResolutionError
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/errors.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Custom error classes for @prmichaelsen/mcp-auth\n * \n * Provides specific error types for different failure scenarios.\n */\n\n/**\n * Base error class for all mcp-auth errors\n */\nexport class MCPAuthError extends Error {\n /**\n * Error code for programmatic handling\n */\n public readonly code: string;\n \n /**\n * HTTP status code (for HTTP/SSE transports)\n */\n public readonly statusCode: number;\n \n /**\n * Additional error details\n */\n public readonly details?: Record<string, unknown>;\n \n constructor(\n message: string,\n code: string = 'MCP_AUTH_ERROR',\n statusCode: number = 500,\n details?: Record<string, unknown>\n ) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.statusCode = statusCode;\n this.details = details;\n \n // Maintains proper stack trace for where error was thrown (V8 only)\n if (typeof (Error as any).captureStackTrace === 'function') {\n (Error as any).captureStackTrace(this, this.constructor);\n }\n }\n \n /**\n * Convert error to JSON for logging/transmission\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n statusCode: this.statusCode,\n details: this.details,\n stack: this.stack\n };\n }\n}\n\n/**\n * Authentication failed error\n * Thrown when request authentication fails\n */\nexport class AuthenticationError extends MCPAuthError {\n constructor(message: string = 'Authentication failed', details?: Record<string, unknown>) {\n super(message, 'AUTHENTICATION_FAILED', 401, details);\n }\n}\n\n/**\n * Token resolution failed error\n * Thrown when resource token cannot be resolved for a user\n */\nexport class TokenResolutionError extends MCPAuthError {\n constructor(\n userId: string,\n resourceType: string,\n details?: Record<string, unknown>\n ) {\n super(\n `Failed to resolve ${resourceType} token for user ${userId}`,\n 'TOKEN_RESOLUTION_FAILED',\n 401,\n { userId, resourceType, ...details }\n );\n }\n}\n\n/**\n * Invalid token error\n * Thrown when a token is invalid or expired\n */\nexport class InvalidTokenError extends MCPAuthError {\n constructor(message: string = 'Invalid or expired token', details?: Record<string, unknown>) {\n super(message, 'INVALID_TOKEN', 401, details);\n }\n}\n\n/**\n * Missing credentials error\n * Thrown when required authentication credentials are missing\n */\nexport class MissingCredentialsError extends MCPAuthError {\n constructor(message: string = 'Missing authentication credentials', details?: Record<string, unknown>) {\n super(message, 'MISSING_CREDENTIALS', 401, details);\n }\n}\n\n/**\n * Configuration error\n * Thrown when the server or provider is misconfigured\n */\nexport class ConfigurationError extends MCPAuthError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'CONFIGURATION_ERROR', 500, details);\n }\n}\n\n/**\n * Rate limit exceeded error\n * Thrown when rate limit is exceeded\n */\nexport class RateLimitError extends MCPAuthError {\n constructor(\n message: string = 'Rate limit exceeded',\n retryAfter?: number,\n details?: Record<string, unknown>\n ) {\n super(\n message,\n 'RATE_LIMIT_EXCEEDED',\n 429,\n { retryAfter, ...details }\n );\n }\n}\n\n/**\n * Server pooling error\n * Thrown when server pool operations fail\n */\nexport class ServerPoolError extends MCPAuthError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'SERVER_POOL_ERROR', 500, details);\n }\n}\n\n/**\n * Transport error\n * Thrown when transport-related operations fail\n */\nexport class TransportError extends MCPAuthError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'TRANSPORT_ERROR', 500, details);\n }\n}\n\n/**\n * Validation error\n * Thrown when input validation fails\n */\nexport class ValidationError extends MCPAuthError {\n constructor(message: string, details?: Record<string, unknown>) {\n super(message, 'VALIDATION_ERROR', 400, details);\n }\n}\n\n/**\n * Type guard to check if an error is an MCPAuthError\n */\nexport function isMCPAuthError(error: unknown): error is MCPAuthError {\n return error instanceof MCPAuthError;\n}\n\n/**\n * Type guard to check if an error is an authentication error\n */\nexport function isAuthenticationError(error: unknown): error is AuthenticationError {\n return error instanceof AuthenticationError;\n}\n\n/**\n * Type guard to check if an error is a token resolution error\n */\nexport function isTokenResolutionError(error: unknown): error is TokenResolutionError {\n return error instanceof TokenResolutionError;\n}\n\n/**\n * Type guard to check if an error is a rate limit error\n */\nexport function isRateLimitError(error: unknown): error is RateLimitError {\n return error instanceof RateLimitError;\n}\n\n/**\n * Format error for client response\n * Sanitizes sensitive information from error details\n */\nexport function formatErrorForClient(error: unknown): {\n error: string;\n code: string;\n statusCode: number;\n} {\n if (isMCPAuthError(error)) {\n return {\n error: error.message,\n code: error.code,\n statusCode: error.statusCode\n };\n }\n \n if (error instanceof Error) {\n return {\n error: error.message,\n code: 'INTERNAL_ERROR',\n statusCode: 500\n };\n }\n \n return {\n error: 'An unknown error occurred',\n code: 'UNKNOWN_ERROR',\n statusCode: 500\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MCPAuthError,
|
|
3
|
+
AuthenticationError,
|
|
4
|
+
TokenResolutionError,
|
|
5
|
+
InvalidTokenError,
|
|
6
|
+
MissingCredentialsError,
|
|
7
|
+
ConfigurationError,
|
|
8
|
+
RateLimitError,
|
|
9
|
+
ServerPoolError,
|
|
10
|
+
TransportError,
|
|
11
|
+
ValidationError,
|
|
12
|
+
isMCPAuthError,
|
|
13
|
+
isAuthenticationError,
|
|
14
|
+
isTokenResolutionError,
|
|
15
|
+
isRateLimitError,
|
|
16
|
+
formatErrorForClient
|
|
17
|
+
} from "./errors.js";
|
|
18
|
+
import {
|
|
19
|
+
Logger,
|
|
20
|
+
LogLevel,
|
|
21
|
+
defaultLogger,
|
|
22
|
+
createLogger,
|
|
23
|
+
sanitizeForLogging
|
|
24
|
+
} from "./logger.js";
|
|
25
|
+
import {
|
|
26
|
+
validateNonEmptyString,
|
|
27
|
+
validateUrl,
|
|
28
|
+
validatePositiveNumber,
|
|
29
|
+
validatePort,
|
|
30
|
+
validateEnum,
|
|
31
|
+
validateObject,
|
|
32
|
+
validateFunction,
|
|
33
|
+
validateRequiredFields,
|
|
34
|
+
validateTransportConfig,
|
|
35
|
+
validateRateLimitConfig,
|
|
36
|
+
validateLoggingConfig,
|
|
37
|
+
validatePoolingConfig,
|
|
38
|
+
sanitizeString,
|
|
39
|
+
validateUserId,
|
|
40
|
+
validateResourceType,
|
|
41
|
+
validateAccessToken
|
|
42
|
+
} from "./validation.js";
|
|
43
|
+
export {
|
|
44
|
+
AuthenticationError,
|
|
45
|
+
ConfigurationError,
|
|
46
|
+
InvalidTokenError,
|
|
47
|
+
LogLevel,
|
|
48
|
+
Logger,
|
|
49
|
+
MCPAuthError,
|
|
50
|
+
MissingCredentialsError,
|
|
51
|
+
RateLimitError,
|
|
52
|
+
ServerPoolError,
|
|
53
|
+
TokenResolutionError,
|
|
54
|
+
TransportError,
|
|
55
|
+
ValidationError,
|
|
56
|
+
createLogger,
|
|
57
|
+
defaultLogger,
|
|
58
|
+
formatErrorForClient,
|
|
59
|
+
isAuthenticationError,
|
|
60
|
+
isMCPAuthError,
|
|
61
|
+
isRateLimitError,
|
|
62
|
+
isTokenResolutionError,
|
|
63
|
+
sanitizeForLogging,
|
|
64
|
+
sanitizeString,
|
|
65
|
+
validateAccessToken,
|
|
66
|
+
validateEnum,
|
|
67
|
+
validateFunction,
|
|
68
|
+
validateLoggingConfig,
|
|
69
|
+
validateNonEmptyString,
|
|
70
|
+
validateObject,
|
|
71
|
+
validatePoolingConfig,
|
|
72
|
+
validatePort,
|
|
73
|
+
validatePositiveNumber,
|
|
74
|
+
validateRateLimitConfig,
|
|
75
|
+
validateRequiredFields,
|
|
76
|
+
validateResourceType,
|
|
77
|
+
validateTransportConfig,
|
|
78
|
+
validateUrl,
|
|
79
|
+
validateUserId
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Utilities module exports\n */\n\n// Errors\nexport {\n MCPAuthError,\n AuthenticationError,\n TokenResolutionError,\n InvalidTokenError,\n MissingCredentialsError,\n ConfigurationError,\n RateLimitError,\n ServerPoolError,\n TransportError,\n ValidationError,\n isMCPAuthError,\n isAuthenticationError,\n isTokenResolutionError,\n isRateLimitError,\n formatErrorForClient\n} from './errors.js';\n\n// Logger\nexport {\n Logger,\n LogLevel,\n defaultLogger,\n createLogger,\n sanitizeForLogging,\n type LogEntry\n} from './logger.js';\n\n// Validation\nexport {\n validateNonEmptyString,\n validateUrl,\n validatePositiveNumber,\n validatePort,\n validateEnum,\n validateObject,\n validateFunction,\n validateRequiredFields,\n validateTransportConfig,\n validateRateLimitConfig,\n validateLoggingConfig,\n validatePoolingConfig,\n sanitizeString,\n validateUserId,\n validateResourceType,\n validateAccessToken\n} from './validation.js';\n"],
|
|
5
|
+
"mappings": "AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|