toolception 0.2.5 → 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.
@@ -0,0 +1,65 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { CreatePermissionBasedMcpServerOptions } from '../types/index.js';
3
+ /**
4
+ * Creates an MCP server with permission-based toolset access control.
5
+ *
6
+ * This function provides a separate API for creating servers where each client receives
7
+ * only the toolsets they're authorized to access. Each client gets a fresh server instance
8
+ * with STATIC mode configured to their allowed toolsets, ensuring per-client isolation
9
+ * without meta-tools or dynamic loading.
10
+ *
11
+ * The server supports two permission sources:
12
+ * - **Header-based**: Permissions are read from request headers (e.g., `mcp-toolset-permissions`)
13
+ * - **Config-based**: Permissions are resolved server-side using static maps or resolver functions
14
+ *
15
+ * @param options - Configuration options including permission settings, catalog, and HTTP transport options
16
+ * @returns Server instance with `server`, `start()`, and `close()` methods matching the createMcpServer interface
17
+ * @throws {Error} If permission configuration is invalid, missing, or if startup.mode is provided
18
+ *
19
+ * @example
20
+ * // Header-based permissions
21
+ * const server = await createPermissionBasedMcpServer({
22
+ * createServer: () => new McpServer({ name: "my-server", version: "1.0.0" }),
23
+ * catalog: { toolsetA: { name: "Toolset A", tools: [...] } },
24
+ * permissions: {
25
+ * source: 'headers',
26
+ * headerName: 'mcp-toolset-permissions' // optional, this is the default
27
+ * }
28
+ * });
29
+ *
30
+ * @example
31
+ * // Config-based permissions with static map
32
+ * const server = await createPermissionBasedMcpServer({
33
+ * createServer: () => new McpServer({ name: "my-server", version: "1.0.0" }),
34
+ * catalog: { toolsetA: { name: "Toolset A", tools: [...] } },
35
+ * permissions: {
36
+ * source: 'config',
37
+ * staticMap: {
38
+ * 'client-1': ['toolsetA', 'toolsetB'],
39
+ * 'client-2': ['toolsetC']
40
+ * },
41
+ * defaultPermissions: [] // optional, defaults to empty array
42
+ * }
43
+ * });
44
+ *
45
+ * @example
46
+ * // Config-based permissions with resolver function
47
+ * const server = await createPermissionBasedMcpServer({
48
+ * createServer: () => new McpServer({ name: "my-server", version: "1.0.0" }),
49
+ * catalog: { toolsetA: { name: "Toolset A", tools: [...] } },
50
+ * permissions: {
51
+ * source: 'config',
52
+ * resolver: (clientId) => {
53
+ * // Your custom logic to determine permissions
54
+ * return clientId.startsWith('admin-') ? ['toolsetA', 'toolsetB'] : ['toolsetA'];
55
+ * },
56
+ * defaultPermissions: ['toolsetA'] // fallback if resolver fails
57
+ * }
58
+ * });
59
+ */
60
+ export declare function createPermissionBasedMcpServer(options: CreatePermissionBasedMcpServerOptions): Promise<{
61
+ server: McpServer;
62
+ start: () => Promise<void>;
63
+ close: () => Promise<void>;
64
+ }>;
65
+ //# sourceMappingURL=createPermissionBasedMcpServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createPermissionBasedMcpServer.d.ts","sourceRoot":"","sources":["../../src/server/createPermissionBasedMcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,mBAAmB,CAAC;AAO/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,qCAAqC;;;;GA6F/C"}
@@ -1,3 +1,4 @@
1
+ import { CreateMcpServerOptions } from '../server/createMcpServer.js';
1
2
  export type McpToolDefinition = {
2
3
  name: string;
3
4
  description: string;
@@ -22,4 +23,232 @@ export type ExposurePolicy = {
22
23
  };
23
24
  export type ToolingErrorCode = "E_VALIDATION" | "E_POLICY_MAX_ACTIVE" | "E_TOOL_NAME_CONFLICT" | "E_NOTIFY_FAILED" | "E_INTERNAL";
24
25
  export type ModuleLoader = (context?: unknown) => Promise<McpToolDefinition[]> | McpToolDefinition[];
26
+ /**
27
+ * Configuration for permission-based toolset access control.
28
+ *
29
+ * Defines how client permissions are resolved and applied when using
30
+ * `createPermissionBasedMcpServer`. Supports two permission sources:
31
+ *
32
+ * **Header-based permissions**: Permissions are extracted from request headers.
33
+ * This approach is simpler but requires proper authentication/authorization in your
34
+ * application layer to prevent header tampering.
35
+ *
36
+ * **Config-based permissions**: Permissions are resolved server-side using either
37
+ * a static map or a resolver function. This provides stronger security by not
38
+ * trusting client-provided permission data.
39
+ *
40
+ * @example Header-based configuration
41
+ * ```typescript
42
+ * const config: PermissionConfig = {
43
+ * source: 'headers',
44
+ * headerName: 'mcp-toolset-permissions' // optional, this is the default
45
+ * };
46
+ * // Client sends: mcp-toolset-permissions: toolset-a,toolset-b
47
+ * ```
48
+ *
49
+ * @example Config-based with static map
50
+ * ```typescript
51
+ * const config: PermissionConfig = {
52
+ * source: 'config',
53
+ * staticMap: {
54
+ * 'client-1': ['toolset-a', 'toolset-b'],
55
+ * 'client-2': ['toolset-c']
56
+ * },
57
+ * defaultPermissions: [] // optional, for unknown clients
58
+ * };
59
+ * ```
60
+ *
61
+ * @example Config-based with resolver function
62
+ * ```typescript
63
+ * const config: PermissionConfig = {
64
+ * source: 'config',
65
+ * resolver: (clientId: string) => {
66
+ * // Custom logic to determine permissions
67
+ * if (clientId.startsWith('admin-')) {
68
+ * return ['toolset-a', 'toolset-b', 'toolset-c'];
69
+ * }
70
+ * return ['toolset-a'];
71
+ * },
72
+ * defaultPermissions: ['toolset-a'] // fallback if resolver fails
73
+ * };
74
+ * ```
75
+ */
76
+ export type PermissionConfig = {
77
+ /**
78
+ * The source of permission data.
79
+ *
80
+ * - `'headers'`: Read permissions from request headers. Requires proper authentication
81
+ * in your application layer to prevent tampering.
82
+ * - `'config'`: Use server-side permission configuration via staticMap or resolver.
83
+ * Provides stronger security by not trusting client-provided data.
84
+ */
85
+ source: "headers" | "config";
86
+ /**
87
+ * Name of the header containing permission data (for header-based permissions).
88
+ *
89
+ * The header value should be a comma-separated list of toolset names.
90
+ * Only used when `source` is `'headers'`.
91
+ *
92
+ * @default 'mcp-toolset-permissions'
93
+ * @example
94
+ * ```typescript
95
+ * // With default header name
96
+ * { source: 'headers' }
97
+ * // Client sends: mcp-toolset-permissions: toolset-a,toolset-b
98
+ *
99
+ * // With custom header name
100
+ * { source: 'headers', headerName: 'x-allowed-toolsets' }
101
+ * // Client sends: x-allowed-toolsets: toolset-a,toolset-b
102
+ * ```
103
+ */
104
+ headerName?: string;
105
+ /**
106
+ * Static mapping of client IDs to their allowed toolsets (for config-based permissions).
107
+ *
108
+ * Each key is a client ID, and the value is an array of toolset names the client can access.
109
+ * Only used when `source` is `'config'`. At least one of `staticMap` or `resolver` must
110
+ * be provided for config-based permissions.
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * {
115
+ * source: 'config',
116
+ * staticMap: {
117
+ * 'client-1': ['toolset-a', 'toolset-b'],
118
+ * 'client-2': ['toolset-c'],
119
+ * 'admin-user': ['toolset-a', 'toolset-b', 'toolset-c']
120
+ * }
121
+ * }
122
+ * ```
123
+ */
124
+ staticMap?: Record<string, string[]>;
125
+ /**
126
+ * Synchronous function to resolve permissions for a client (for config-based permissions).
127
+ *
128
+ * Called with the client ID and should return an array of allowed toolset names.
129
+ * Only used when `source` is `'config'`. If both `staticMap` and `resolver` are provided,
130
+ * the resolver takes precedence with staticMap as fallback.
131
+ *
132
+ * The function should be synchronous and deterministic. If it throws an error or returns
133
+ * a non-array value, the system falls back to `staticMap` or `defaultPermissions`.
134
+ *
135
+ * @param clientId - The unique identifier for the client requesting access
136
+ * @returns Array of toolset names the client is allowed to access
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * {
141
+ * source: 'config',
142
+ * resolver: (clientId: string) => {
143
+ * // Integrate with your auth system
144
+ * const user = getUserFromCache(clientId);
145
+ * if (user.role === 'admin') {
146
+ * return ['toolset-a', 'toolset-b', 'toolset-c'];
147
+ * } else if (user.role === 'user') {
148
+ * return ['toolset-a'];
149
+ * }
150
+ * return [];
151
+ * }
152
+ * }
153
+ * ```
154
+ */
155
+ resolver?: (clientId: string) => string[];
156
+ /**
157
+ * Default permissions to apply when a client is not found in staticMap or resolver fails.
158
+ *
159
+ * Used as a fallback when:
160
+ * - Client ID is not found in `staticMap`
161
+ * - `resolver` function throws an error or returns invalid data
162
+ * - No other permission source provides valid permissions
163
+ *
164
+ * If not specified, defaults to an empty array (no toolsets allowed).
165
+ *
166
+ * @default []
167
+ * @example
168
+ * ```typescript
169
+ * {
170
+ * source: 'config',
171
+ * staticMap: { 'known-client': ['toolset-a'] },
172
+ * defaultPermissions: ['public-toolset'] // Unknown clients get this
173
+ * }
174
+ * ```
175
+ */
176
+ defaultPermissions?: string[];
177
+ };
178
+ /**
179
+ * Options for creating a permission-based MCP server.
180
+ *
181
+ * Extends `CreateMcpServerOptions` but removes the `startup` field and requires
182
+ * a `permissions` configuration. Permission-based servers automatically use DYNAMIC
183
+ * mode internally to ensure per-client isolation, with each client receiving only
184
+ * their authorized toolsets.
185
+ *
186
+ * All standard options from `CreateMcpServerOptions` are supported, including:
187
+ * - `createServer`: Factory function to create MCP server instances
188
+ * - `catalog`: Toolset catalog defining available toolsets
189
+ * - `moduleLoaders`: Optional lazy-loading modules for toolsets
190
+ * - `exposurePolicy`: Optional policy for toolset exposure control
191
+ * - `http`: HTTP transport configuration (host, port, CORS, etc.)
192
+ * - `context`: Optional context passed to module loaders
193
+ *
194
+ * @example Basic header-based server
195
+ * ```typescript
196
+ * const options: CreatePermissionBasedMcpServerOptions = {
197
+ * createServer: () => new McpServer({ name: "my-server", version: "1.0.0" }),
198
+ * catalog: {
199
+ * 'toolset-a': { name: 'Toolset A', tools: [...] },
200
+ * 'toolset-b': { name: 'Toolset B', tools: [...] }
201
+ * },
202
+ * permissions: {
203
+ * source: 'headers'
204
+ * },
205
+ * http: {
206
+ * port: 3000,
207
+ * cors: true
208
+ * }
209
+ * };
210
+ * ```
211
+ *
212
+ * @example Config-based server with static map
213
+ * ```typescript
214
+ * const options: CreatePermissionBasedMcpServerOptions = {
215
+ * createServer: () => new McpServer({ name: "my-server", version: "1.0.0" }),
216
+ * catalog: {
217
+ * 'toolset-a': { name: 'Toolset A', tools: [...] },
218
+ * 'toolset-b': { name: 'Toolset B', tools: [...] }
219
+ * },
220
+ * permissions: {
221
+ * source: 'config',
222
+ * staticMap: {
223
+ * 'client-1': ['toolset-a'],
224
+ * 'client-2': ['toolset-a', 'toolset-b']
225
+ * },
226
+ * defaultPermissions: []
227
+ * }
228
+ * };
229
+ * ```
230
+ */
231
+ export type CreatePermissionBasedMcpServerOptions = Omit<CreateMcpServerOptions, "startup"> & {
232
+ /**
233
+ * Permission configuration defining how client access control is enforced.
234
+ *
235
+ * This field is required for permission-based servers. It determines whether
236
+ * permissions are read from request headers or resolved server-side using
237
+ * static maps or resolver functions.
238
+ *
239
+ * @see {@link PermissionConfig} for detailed configuration options and examples
240
+ */
241
+ permissions: PermissionConfig;
242
+ /**
243
+ * Startup configuration is not allowed for permission-based servers.
244
+ *
245
+ * Permission-based servers automatically determine which toolsets to load for
246
+ * each client based on the `permissions` configuration. The server internally
247
+ * uses STATIC mode per client to ensure isolation and prevent dynamic toolset
248
+ * changes during a session.
249
+ *
250
+ * @deprecated Do not use - permission-based servers determine toolsets from client permissions
251
+ */
252
+ startup?: never;
253
+ };
25
254
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAE5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAE/D,MAAM,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,qBAAqB,GACrB,sBAAsB,GACtB,iBAAiB,GACjB,YAAY,CAAC;AAKjB,MAAM,MAAM,YAAY,GAAG,CACzB,OAAO,CAAC,EAAE,OAAO,KACd,OAAO,CAAC,iBAAiB,EAAE,CAAC,GAAG,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAI3E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAE5B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAE/D,MAAM,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,qBAAqB,GACrB,sBAAsB,GACtB,iBAAiB,GACjB,YAAY,CAAC;AAKjB,MAAM,MAAM,YAAY,GAAG,CACzB,OAAO,CAAC,EAAE,OAAO,KACd,OAAO,CAAC,iBAAiB,EAAE,CAAC,GAAG,iBAAiB,EAAE,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;;;;OAOG;IACH,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;IAE7B;;;;;;;;;;;;;;;;;OAiBG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAE1C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,MAAM,qCAAqC,GAAG,IAAI,CACtD,sBAAsB,EACtB,SAAS,CACV,GAAG;IACF;;;;;;;;OAQG;IACH,WAAW,EAAE,gBAAgB,CAAC;IAE9B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolception",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -58,6 +58,11 @@
58
58
  "dynamic-tools",
59
59
  "toolset",
60
60
  "tool-management",
61
+ "permissions",
62
+ "authentication",
63
+ "access-control",
64
+ "security",
65
+ "authorization",
61
66
  "json-rpc",
62
67
  "fastify",
63
68
  "zod",