@jalpp/mcp-adapter 0.3.0 → 0.3.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @jalpp/mcp-adapter
2
2
 
3
- Lightweight adapter utilities for registering tools on an [MCP](https://modelcontextprotocol.io) server with full TypeScript type safety. Supports manual tool registration and automatic HTTP endpoint-to-tool bridging with built-in auth, path variables, and method-specific adapters.
3
+ Lightweight adapter utilities for registering tools and resources on an [MCP](https://modelcontextprotocol.io) server with full TypeScript type safety. Supports manual tool registration, automatic HTTP endpoint-to-tool bridging with built-in auth, path variables, method-specific adapters, and MCP resource registration.
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,37 +14,48 @@ npm install @jalpp/mcp-adapter
14
14
  npm install @modelcontextprotocol/sdk zod axios
15
15
  ```
16
16
 
17
+ ---
18
+
17
19
  ## Adapters
18
20
 
19
- | Adapter | Method | Description |
20
- |---------|--------|-------------|
21
- | `toolAdapter` | any | Register a tool with a typed callback |
22
- | `toolContentAdapter` | — | Normalize a result into a `CallToolResult` |
23
- | `httpToolAdapter` | any | Register any HTTP endpoint as a tool |
24
- | `getToolAdapter` | GET | Register a GET endpoint args → query params |
25
- | `postToolAdapter` | POST | Register a POST endpoint args request body |
26
- | `putToolAdapter` | PUT | Register a PUT endpoint — args → request body |
27
- | `patchToolAdapter` | PATCH | Register a PATCH endpoint — args → request body |
28
- | `deleteToolAdapter` | DELETE | Register a DELETE endpoint — args → query params |
21
+ ### Tool adapters
22
+
23
+ | Adapter | Description |
24
+ |---------|-------------|
25
+ | `toolAdapter` | Registers a tool with a typed callback |
26
+ | `toolContentAdapter` | Normalizes a result into a `CallToolResult` |
27
+ | `httpToolAdapter` | Registers any HTTP endpoint as a tool |
28
+ | `getToolAdapter` | Registers a GET endpoint as a tool — args → query params |
29
+ | `postToolAdapter` | Registers a POST endpoint as a tool — args → request body |
30
+ | `putToolAdapter` | Registers a PUT endpoint as a tool — args → request body |
31
+ | `patchToolAdapter` | Registers a PATCH endpoint as a tool — args → request body |
32
+ | `deleteToolAdapter` | Registers a DELETE endpoint as a tool — args → query params |
33
+
34
+ ### Resource adapters
35
+
36
+ | Adapter | Description |
37
+ |---------|-------------|
38
+ | `staticResourceAdapter` | Registers a static MCP resource with a fixed URI |
39
+ | `dynamicResourceAdapter` | Registers a dynamic MCP resource with a URI template |
29
40
 
30
41
  ---
31
42
 
32
43
  ## Path Variables
33
44
 
34
- All HTTP adapters support `:paramName` path variable syntax. Any input arg whose name matches a path variable is interpolated into the URL and removed from query params / request body.
45
+ All HTTP tool adapters support `:paramName` path variable syntax. Any input arg whose name matches a path variable is interpolated into the URL and removed from query params / request body.
35
46
 
36
47
  ```ts
37
48
  getToolAdapter(server, {
38
- name: "get-game-details",
39
- description: "Fetch a game by ID",
40
- endpoint: "https://api.example.com/games/:gameId",
49
+ name: "get-user",
50
+ description: "Fetch a user by ID",
51
+ endpoint: "https://api.example.com/users/:userId",
41
52
  inputSchema: {
42
- gameId: z.string().describe("Game ID"),
53
+ userId: z.string().describe("User ID"),
43
54
  expand: z.string().optional().describe("Optional fields to expand"),
44
55
  },
45
56
  auth: { type: "bearer", token: process.env.API_TOKEN! },
46
57
  });
47
- // GET https://api.example.com/games/abc123?expand=moves
58
+ // Registers get-user tool which calls GET https://api.example.com/users/abc123?expand=profile
48
59
  ```
49
60
 
50
61
  ---
@@ -67,7 +78,7 @@ getToolAdapter(server, {
67
78
  },
68
79
  auth: { type: "bearer", token: process.env.API_TOKEN! },
69
80
  });
70
- // GET https://api.example.com/users/abc123?expand=profile
81
+ // Registers get-user tool which calls GET https://api.example.com/users/abc123?expand=profile
71
82
  ```
72
83
 
73
84
  ---
@@ -90,7 +101,7 @@ postToolAdapter(server, {
90
101
  },
91
102
  auth: { type: "bearer", token: process.env.API_TOKEN! },
92
103
  });
93
- // POST https://api.example.com/users/abc123/posts { title, body }
104
+ // Registers create-post tool which calls POST https://api.example.com/users/abc123/posts { title, body }
94
105
  ```
95
106
 
96
107
  ---
@@ -100,6 +111,8 @@ postToolAdapter(server, {
100
111
  Registers a PUT endpoint as an MCP tool. Remaining args are sent as the **JSON request body**.
101
112
 
102
113
  ```ts
114
+ import { putToolAdapter } from "@jalpp/mcp-adapter";
115
+
103
116
  putToolAdapter(server, {
104
117
  name: "update-user",
105
118
  description: "Replace a user record",
@@ -111,7 +124,7 @@ putToolAdapter(server, {
111
124
  },
112
125
  auth: { type: "bearer", token: process.env.API_TOKEN! },
113
126
  });
114
- // PUT https://api.example.com/users/abc123 { name, email }
127
+ // Registers update-user tool which calls PUT https://api.example.com/users/abc123 { name, email }
115
128
  ```
116
129
 
117
130
  ---
@@ -121,6 +134,8 @@ putToolAdapter(server, {
121
134
  Registers a PATCH endpoint as an MCP tool. Remaining args are sent as the **JSON request body**.
122
135
 
123
136
  ```ts
137
+ import { patchToolAdapter } from "@jalpp/mcp-adapter";
138
+
124
139
  patchToolAdapter(server, {
125
140
  name: "update-post-title",
126
141
  description: "Partially update a post",
@@ -131,7 +146,7 @@ patchToolAdapter(server, {
131
146
  },
132
147
  auth: { type: "bearer", token: process.env.API_TOKEN! },
133
148
  });
134
- // PATCH https://api.example.com/posts/xyz789 { title }
149
+ // Registers update-post-title tool which calls PATCH https://api.example.com/posts/xyz789 { title }
135
150
  ```
136
151
 
137
152
  ---
@@ -141,6 +156,8 @@ patchToolAdapter(server, {
141
156
  Registers a DELETE endpoint as an MCP tool. Remaining args (after path variable interpolation) are sent as **query parameters**.
142
157
 
143
158
  ```ts
159
+ import { deleteToolAdapter } from "@jalpp/mcp-adapter";
160
+
144
161
  deleteToolAdapter(server, {
145
162
  name: "delete-post",
146
163
  description: "Delete a post by ID",
@@ -148,26 +165,27 @@ deleteToolAdapter(server, {
148
165
  inputSchema: { postId: z.string() },
149
166
  auth: { type: "bearer", token: process.env.API_TOKEN! },
150
167
  });
151
- // DELETE https://api.example.com/posts/xyz789
168
+ // Registers delete-post tool which calls DELETE https://api.example.com/posts/xyz789
152
169
  ```
153
170
 
154
171
  ---
155
172
 
156
173
  ## `httpToolAdapter`
157
174
 
158
- Lower-level adapter that accepts an explicit `method` field. Use this when the method needs to be dynamic or you prefer a single unified call style.
175
+ Lower-level adapter that accepts an explicit `method` field. Use this when you prefer a single unified call style or need to pass the method dynamically.
159
176
 
160
177
  ```ts
161
178
  import { httpToolAdapter } from "@jalpp/mcp-adapter";
162
179
 
163
180
  httpToolAdapter(server, {
164
- name: "get-analysis",
165
- description: "Fetch position analysis",
166
- endpoint: "https://api.example.com/analyze",
181
+ name: "search-products",
182
+ description: "Search the product catalogue",
183
+ endpoint: "https://api.example.com/products/search",
167
184
  method: "POST",
168
- inputSchema: { fen: z.string(), depth: z.number() },
169
- auth: { type: "bearer", token: process.env.API_TOKEN! },
185
+ inputSchema: { query: z.string(), limit: z.number().optional() },
186
+ auth: { type: "apikey", header: "X-API-Key", key: process.env.API_KEY! },
170
187
  });
188
+ // Registers search-products tool which calls POST https://api.example.com/products/search { query, limit }
171
189
  ```
172
190
 
173
191
  ---
@@ -180,19 +198,19 @@ Registers a tool with a fully custom typed callback. Use when you need data tran
180
198
 
181
199
  ```ts
182
200
  import { toolAdapter, toolContentAdapter } from "@jalpp/mcp-adapter";
201
+ import z from "zod";
183
202
 
184
203
  toolAdapter(server, {
185
- name: "get-analysis",
204
+ name: "summarize-report",
186
205
  config: {
187
- description: "Analyze a chess position",
206
+ description: "Fetch and summarize a sales report",
188
207
  inputSchema: {
189
- fen: z.string().describe("FEN string"),
190
- depth: z.number().min(1).max(20),
208
+ reportId: z.string().describe("Report ID"),
209
+ format: z.enum(["short", "detailed"]).default("short"),
191
210
  },
192
- annotations: { openWorldHint: true },
193
211
  },
194
- cb: async ({ fen, depth }) => {
195
- const { data, error } = await myService.analyze(fen, depth);
212
+ cb: async ({ reportId, format }) => {
213
+ const { data, error } = await reportService.get(reportId, format);
196
214
  return toolContentAdapter(data ?? {}, error);
197
215
  },
198
216
  });
@@ -202,10 +220,10 @@ toolAdapter(server, {
202
220
 
203
221
  ```ts
204
222
  toolAdapter(server, {
205
- name: "get-status",
223
+ name: "get-server-status",
206
224
  config: { description: "Returns current server status" },
207
225
  cb: async () => {
208
- const { data, error } = await myService.getStatus();
226
+ const { data, error } = await statusService.get();
209
227
  return toolContentAdapter(data ?? {}, error);
210
228
  },
211
229
  });
@@ -215,7 +233,7 @@ toolAdapter(server, {
215
233
 
216
234
  ## `toolContentAdapter`
217
235
 
218
- Normalizes a result into a `CallToolResult` text block. If `error` is defined it takes priority; otherwise `data` is serialized as pretty-printed JSON.
236
+ Normalizes a service result into a `CallToolResult` text block. If `error` is defined it takes priority; otherwise `data` is serialized as pretty-printed JSON.
219
237
 
220
238
  ```ts
221
239
  return toolContentAdapter(data ?? {}, error);
@@ -228,9 +246,79 @@ return toolContentAdapter(data ?? {}, error);
228
246
 
229
247
  ---
230
248
 
249
+ ## `staticResourceAdapter`
250
+
251
+ Registers a static MCP resource with a fixed URI. The `load` callback is called on every client request and may return fresh content each time. Use this for resources whose identity is fixed but content may change (e.g. a config file, a status page, a knowledge base).
252
+
253
+ ```ts
254
+ import { staticResourceAdapter } from "@jalpp/mcp-adapter";
255
+
256
+ // Expose a live system health report
257
+ staticResourceAdapter(server, {
258
+ name: "system-health",
259
+ uri: "status://health",
260
+ title: "System Health",
261
+ description: "Live health status of all services",
262
+ mimeType: "application/json",
263
+ load: async () => JSON.stringify(await healthService.getReport()),
264
+ });
265
+
266
+ // Expose a markdown documentation page
267
+ staticResourceAdapter(server, {
268
+ name: "api-docs",
269
+ uri: "docs://api",
270
+ title: "API Documentation",
271
+ description: "REST API reference documentation",
272
+ mimeType: "text/markdown",
273
+ load: () => fs.readFileSync("./docs/api.md", "utf-8"),
274
+ });
275
+ ```
276
+
277
+ ---
278
+
279
+ ## `dynamicResourceAdapter`
280
+
281
+ Registers a dynamic MCP resource with a URI template. Use `{paramName}` placeholders — matched values are extracted and passed to the `load` callback. Use this for resources identified by an ID or other variable.
282
+
283
+ ```ts
284
+ import { dynamicResourceAdapter } from "@jalpp/mcp-adapter";
285
+
286
+ // Expose a user profile by ID
287
+ dynamicResourceAdapter(server, {
288
+ name: "user-profile",
289
+ uriTemplate: "users://{userId}/profile",
290
+ title: "User Profile",
291
+ description: "Profile data for a specific user",
292
+ mimeType: "application/json",
293
+ load: async (uri, { userId }) => JSON.stringify(await userService.getProfile(userId)),
294
+ });
295
+
296
+ // Expose an order invoice by order ID
297
+ dynamicResourceAdapter(server, {
298
+ name: "order-invoice",
299
+ uriTemplate: "orders://{orderId}/invoice",
300
+ title: "Order Invoice",
301
+ description: "Invoice details for a specific order",
302
+ mimeType: "application/json",
303
+ load: async (uri, { orderId }) => JSON.stringify(await orderService.getInvoice(orderId)),
304
+ });
305
+
306
+ // Expose a blog post by slug
307
+ dynamicResourceAdapter(server, {
308
+ name: "blog-post",
309
+ uriTemplate: "blog://{slug}",
310
+ title: "Blog Post",
311
+ description: "Markdown content for a blog post",
312
+ mimeType: "text/markdown",
313
+ load: async (uri, { slug }) => await blogService.getPostMarkdown(slug),
314
+ });
315
+ ```
316
+
317
+ ---
318
+
231
319
  ## Authentication
232
320
 
233
- All HTTP adapters accept an optional `auth` field. Three strategies are supported:
321
+ All HTTP tool adapters accept an optional `auth` field. Three strategies are supported:
234
322
 
235
323
  **Bearer token** — `Authorization: Bearer <token>`:
236
324
  ```ts
@@ -266,17 +354,26 @@ getToolAdapter(server, {
266
354
 
267
355
  ## API Reference
268
356
 
269
- ### Method-specific adapters
357
+ ### Tool adapters
358
+
359
+ | Function | Registers | Args mapping |
360
+ |----------|-----------|--------------|
361
+ | `getToolAdapter` | GET tool | remaining args → query params |
362
+ | `postToolAdapter` | POST tool | remaining args → request body |
363
+ | `putToolAdapter` | PUT tool | remaining args → request body |
364
+ | `patchToolAdapter` | PATCH tool | remaining args → request body |
365
+ | `deleteToolAdapter` | DELETE tool | remaining args → query params |
366
+ | `httpToolAdapter` | any method tool | depends on method |
367
+ | `toolAdapter` | custom callback tool | fully custom |
368
+
369
+ All HTTP adapters support optional `inputSchema` and `:paramName` path variables.
270
370
 
271
- | Function | Method | Args mapping |
272
- |----------|--------|--------------|
273
- | `getToolAdapter` | GET | remaining args → query params |
274
- | `postToolAdapter` | POST | remaining args → request body |
275
- | `putToolAdapter` | PUT | remaining args → request body |
276
- | `patchToolAdapter` | PATCH | remaining args → request body |
277
- | `deleteToolAdapter` | DELETE | remaining args → query params |
371
+ ### Resource adapters
278
372
 
279
- All support optional `inputSchema` and `:paramName` path variables.
373
+ | Function | Registers | URI style |
374
+ |----------|-----------|-----------|
375
+ | `staticResourceAdapter` | fixed-URI resource | `"scheme://path"` |
376
+ | `dynamicResourceAdapter` | templated resource | `"scheme://{param}/path"` |
280
377
 
281
378
  ### Auth types
282
379
 
package/dist/index.d.ts CHANGED
@@ -146,6 +146,20 @@ interface HttpToolAdapterWithSchema<T extends ZodRawShapeCompat> {
146
146
  inputSchema: T;
147
147
  /** Optional authentication strategy. */
148
148
  auth?: HttpAuth;
149
+ /**
150
+ * Optional name of the input parameter whose value will be used as a runtime
151
+ * bearer token (`Authorization: Bearer <value>`). The parameter is consumed and
152
+ * never forwarded as a query param or body field.
153
+ *
154
+ * When both `tokenParam` and a static `auth` are provided, the runtime token
155
+ * takes precedence.
156
+ *
157
+ * @example
158
+ * // Schema includes { token: z.string(), userId: z.string() }
159
+ * tokenParam: "token"
160
+ * // → Authorization: Bearer <value of args.token>
161
+ */
162
+ tokenParam?: string;
149
163
  /** Optional extra axios config (e.g. headers, timeout). */
150
164
  axiosConfig?: AxiosRequestConfig;
151
165
  }
@@ -167,6 +181,11 @@ interface HttpToolAdapterWithoutSchema {
167
181
  inputSchema?: undefined;
168
182
  /** Optional authentication strategy. */
169
183
  auth?: HttpAuth;
184
+ /**
185
+ * Not applicable when there is no input schema — included for type symmetry only.
186
+ * Use the schema variant if you need a runtime token param.
187
+ */
188
+ tokenParam?: never;
170
189
  /** Optional extra axios config (e.g. headers, timeout). */
171
190
  axiosConfig?: AxiosRequestConfig;
172
191
  }
@@ -217,6 +236,8 @@ type DeleteToolAdapterConfig<T extends ZodRawShapeCompat> = Omit<HttpToolAdapter
217
236
  * - Path variables (`:paramName`) are interpolated from input args.
218
237
  * - GET/DELETE: remaining args → query parameters.
219
238
  * - POST/PUT/PATCH: remaining args → JSON request body.
239
+ * - `tokenParam`: if set, the named input field is extracted and used as a runtime
240
+ * bearer token, overriding the static `auth` config.
220
241
  *
221
242
  * @template T - Zod raw shape inferred from `inputSchema`.
222
243
  * @param server - The `McpServer` instance to register the tool on.
@@ -239,10 +260,11 @@ declare function httpToolAdapter(server: McpServer, adapter: HttpToolAdapterWith
239
260
  * name: "get-user",
240
261
  * description: "Fetch a user by ID",
241
262
  * endpoint: "https://api.example.com/users/:userId",
242
- * inputSchema: { userId: z.string(), expand: z.string().optional() },
243
- * auth: { type: "bearer", token: process.env.API_TOKEN! },
263
+ * inputSchema: { userId: z.string(), token: z.string(), expand: z.string().optional() },
264
+ * tokenParam: "token",
244
265
  * });
245
266
  * // GET https://api.example.com/users/abc123?expand=profile
267
+ * // Authorization: Bearer <token>
246
268
  */
247
269
  declare function getToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: GetToolAdapterConfig<T>): void;
248
270
  declare function getToolAdapter(server: McpServer, config: GetToolAdapterConfigNoSchema): void;
@@ -261,10 +283,11 @@ declare function getToolAdapter(server: McpServer, config: GetToolAdapterConfigN
261
283
  * name: "create-post",
262
284
  * description: "Create a new post for a user",
263
285
  * endpoint: "https://api.example.com/users/:userId/posts",
264
- * inputSchema: { userId: z.string(), title: z.string(), body: z.string() },
265
- * auth: { type: "bearer", token: process.env.API_TOKEN! },
286
+ * inputSchema: { userId: z.string(), token: z.string(), title: z.string(), body: z.string() },
287
+ * tokenParam: "token",
266
288
  * });
267
289
  * // POST https://api.example.com/users/abc123/posts { title, body }
290
+ * // Authorization: Bearer <token>
268
291
  */
269
292
  declare function postToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: PostToolAdapterConfig<T>): void;
270
293
  declare function postToolAdapter(server: McpServer, config: PostToolAdapterConfigNoSchema): void;
@@ -283,10 +306,11 @@ declare function postToolAdapter(server: McpServer, config: PostToolAdapterConfi
283
306
  * name: "update-user",
284
307
  * description: "Replace a user record",
285
308
  * endpoint: "https://api.example.com/users/:userId",
286
- * inputSchema: { userId: z.string(), name: z.string(), email: z.string() },
287
- * auth: { type: "bearer", token: process.env.API_TOKEN! },
309
+ * inputSchema: { userId: z.string(), token: z.string(), name: z.string(), email: z.string() },
310
+ * tokenParam: "token",
288
311
  * });
289
312
  * // PUT https://api.example.com/users/abc123 { name, email }
313
+ * // Authorization: Bearer <token>
290
314
  */
291
315
  declare function putToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: PutToolAdapterConfig<T>): void;
292
316
  /**
@@ -304,10 +328,11 @@ declare function putToolAdapter<T extends ZodRawShapeCompat>(server: McpServer,
304
328
  * name: "update-post-title",
305
329
  * description: "Partially update a post",
306
330
  * endpoint: "https://api.example.com/posts/:postId",
307
- * inputSchema: { postId: z.string(), title: z.string() },
308
- * auth: { type: "bearer", token: process.env.API_TOKEN! },
331
+ * inputSchema: { postId: z.string(), token: z.string(), title: z.string() },
332
+ * tokenParam: "token",
309
333
  * });
310
334
  * // PATCH https://api.example.com/posts/xyz789 { title }
335
+ * // Authorization: Bearer <token>
311
336
  */
312
337
  declare function patchToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: PatchToolAdapterConfig<T>): void;
313
338
  /**
@@ -325,11 +350,181 @@ declare function patchToolAdapter<T extends ZodRawShapeCompat>(server: McpServer
325
350
  * name: "delete-post",
326
351
  * description: "Delete a post by ID",
327
352
  * endpoint: "https://api.example.com/posts/:postId",
328
- * inputSchema: { postId: z.string() },
329
- * auth: { type: "bearer", token: process.env.API_TOKEN! },
353
+ * inputSchema: { postId: z.string(), token: z.string() },
354
+ * tokenParam: "token",
330
355
  * });
331
356
  * // DELETE https://api.example.com/posts/xyz789
357
+ * // Authorization: Bearer <token>
332
358
  */
333
359
  declare function deleteToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: DeleteToolAdapterConfig<T>): void;
334
360
 
335
- export { type ApiKeyAuth, type BasicAuth, type BearerAuth, type DeleteToolAdapterConfig, type GetToolAdapterConfig, type GetToolAdapterConfigNoSchema, type HttpAuth, type HttpMethod, type HttpToolAdapterWithSchema, type HttpToolAdapterWithoutSchema, type PatchToolAdapterConfig, type PostToolAdapterConfig, type PostToolAdapterConfigNoSchema, type PutToolAdapterConfig, deleteToolAdapter, getToolAdapter, httpToolAdapter, patchToolAdapter, postToolAdapter, putToolAdapter, toolAdapter, toolContentAdapter };
361
+ /**
362
+ * Supported MIME types for resource content.
363
+ * Extend with additional types as needed.
364
+ */
365
+ type ResourceMimeType = "text/plain" | "text/html" | "text/markdown" | "application/json" | "application/xml" | (string & {});
366
+ /**
367
+ * Configuration for registering a static MCP resource.
368
+ *
369
+ * A static resource has a fixed URI with no variable parameters.
370
+ * Use this for resources whose content may change but whose identity does not
371
+ * (e.g. a config file, a status page, a knowledge base).
372
+ *
373
+ * @example
374
+ * staticResourceAdapter(server, {
375
+ * name: "server-config",
376
+ * uri: "config://server",
377
+ * title: "Server Configuration",
378
+ * description: "Current server configuration and environment settings",
379
+ * mimeType: "application/json",
380
+ * load: async () => JSON.stringify(await configService.get()),
381
+ * });
382
+ */
383
+ interface StaticResourceConfig {
384
+ /** Unique resource name used to identify the resource in the MCP registry. */
385
+ name: string;
386
+ /**
387
+ * The fixed URI that clients use to request this resource.
388
+ * @example "config://server"
389
+ * @example "docs://readme"
390
+ */
391
+ uri: string;
392
+ /** Human-readable display title shown in client UIs. */
393
+ title: string;
394
+ /** Description of what the resource contains, shown to the model. */
395
+ description: string;
396
+ /** MIME type of the resource content. Defaults to `"text/plain"`. */
397
+ mimeType?: ResourceMimeType;
398
+ /**
399
+ * Async function that returns the resource content as a string.
400
+ * Called each time a client requests the resource.
401
+ *
402
+ * @returns The resource content string.
403
+ *
404
+ * @example
405
+ * load: async () => JSON.stringify(await configService.get())
406
+ */
407
+ load: () => Promise<string> | string;
408
+ }
409
+ /**
410
+ * Configuration for registering a dynamic MCP resource with URI template parameters.
411
+ *
412
+ * A dynamic resource uses a URI template with `{paramName}` placeholders.
413
+ * The matched parameter values are passed to the `load` callback.
414
+ * Use this for resources identified by an ID or other variable (e.g. a user profile, an order record).
415
+ *
416
+ * @example
417
+ * dynamicResourceAdapter(server, {
418
+ * name: "user-profile",
419
+ * uriTemplate: "users://{userId}/profile",
420
+ * title: "User Profile",
421
+ * description: "Profile data for a given user",
422
+ * mimeType: "application/json",
423
+ * load: async (uri, { userId }) => JSON.stringify(await userService.getProfile(userId)),
424
+ * });
425
+ */
426
+ interface DynamicResourceConfig {
427
+ /** Unique resource name used to identify the resource in the MCP registry. */
428
+ name: string;
429
+ /**
430
+ * URI template string with `{paramName}` placeholders.
431
+ * @example "users://{userId}/profile"
432
+ * @example "orders://{orderId}/invoice"
433
+ */
434
+ uriTemplate: string;
435
+ /** human-readable display title shown in client UIs. */
436
+ title: string;
437
+ /** Description of what the resource contains, shown to the model. */
438
+ description: string;
439
+ /** MIME type of the resource content. Defaults to `"text/plain"`. */
440
+ mimeType?: ResourceMimeType;
441
+ /**
442
+ * Async function that returns the resource content as a string.
443
+ * Called each time a client requests the resource with a matching URI.
444
+ *
445
+ * @param uri - The full resolved URI of the request (as a `URL` object).
446
+ * @param params - Key-value map of extracted URI template parameters.
447
+ * @returns The resource content string.
448
+ *
449
+ * @example
450
+ * load: async (uri, { orderId }) => JSON.stringify(await orderService.getInvoice(orderId))
451
+ */
452
+ load: (uri: URL, params: Record<string, string>) => Promise<string> | string;
453
+ }
454
+ /**
455
+ * Registers a static MCP resource on an `McpServer`.
456
+ *
457
+ * Static resources have a fixed URI. The `load` callback is invoked on every
458
+ * client request and may return fresh data each time.
459
+ *
460
+ * @param server - The `McpServer` instance to register the resource on.
461
+ * @param config - Static resource configuration.
462
+ *
463
+ * @example
464
+ * // Expose a live system health report
465
+ * staticResourceAdapter(server, {
466
+ * name: "system-health",
467
+ * uri: "status://health",
468
+ * title: "System Health",
469
+ * description: "Live health status of all services",
470
+ * mimeType: "application/json",
471
+ * load: async () => JSON.stringify(await healthService.getReport()),
472
+ * });
473
+ *
474
+ * @example
475
+ * // Expose a static markdown documentation page
476
+ * staticResourceAdapter(server, {
477
+ * name: "api-docs",
478
+ * uri: "docs://api",
479
+ * title: "API Documentation",
480
+ * description: "REST API reference documentation",
481
+ * mimeType: "text/markdown",
482
+ * load: () => fs.readFileSync("./docs/api.md", "utf-8"),
483
+ * });
484
+ */
485
+ declare function staticResourceAdapter(server: McpServer, config: StaticResourceConfig): void;
486
+ /**
487
+ * Registers a dynamic MCP resource with a URI template on an `McpServer`.
488
+ *
489
+ * Dynamic resources use `{paramName}` placeholders in the URI template.
490
+ * Matched parameter values are extracted and passed to the `load` callback.
491
+ *
492
+ * @param server - The `McpServer` instance to register the resource on.
493
+ * @param config - Dynamic resource configuration.
494
+ *
495
+ * @example
496
+ * // Expose a user profile by ID
497
+ * dynamicResourceAdapter(server, {
498
+ * name: "user-profile",
499
+ * uriTemplate: "users://{userId}/profile",
500
+ * title: "User Profile",
501
+ * description: "Profile data for a specific user",
502
+ * mimeType: "application/json",
503
+ * load: async (uri, { userId }) => JSON.stringify(await userService.getProfile(userId)),
504
+ * });
505
+ *
506
+ * @example
507
+ * // Expose an order invoice by order ID
508
+ * dynamicResourceAdapter(server, {
509
+ * name: "order-invoice",
510
+ * uriTemplate: "orders://{orderId}/invoice",
511
+ * title: "Order Invoice",
512
+ * description: "Invoice details for a specific order",
513
+ * mimeType: "application/json",
514
+ * load: async (uri, { orderId }) => JSON.stringify(await orderService.getInvoice(orderId)),
515
+ * });
516
+ *
517
+ * @example
518
+ * // Expose a blog post by slug
519
+ * dynamicResourceAdapter(server, {
520
+ * name: "blog-post",
521
+ * uriTemplate: "blog://{slug}",
522
+ * title: "Blog Post",
523
+ * description: "Markdown content for a blog post",
524
+ * mimeType: "text/markdown",
525
+ * load: async (uri, { slug }) => await blogService.getPostMarkdown(slug),
526
+ * });
527
+ */
528
+ declare function dynamicResourceAdapter(server: McpServer, config: DynamicResourceConfig): void;
529
+
530
+ export { type ApiKeyAuth, type BasicAuth, type BearerAuth, type DeleteToolAdapterConfig, type DynamicResourceConfig, type GetToolAdapterConfig, type GetToolAdapterConfigNoSchema, type HttpAuth, type HttpMethod, type HttpToolAdapterWithSchema, type HttpToolAdapterWithoutSchema, type PatchToolAdapterConfig, type PostToolAdapterConfig, type PostToolAdapterConfigNoSchema, type PutToolAdapterConfig, type ResourceMimeType, type StaticResourceConfig, deleteToolAdapter, dynamicResourceAdapter, getToolAdapter, httpToolAdapter, patchToolAdapter, postToolAdapter, putToolAdapter, staticResourceAdapter, toolAdapter, toolContentAdapter };
package/dist/index.js CHANGED
@@ -51,10 +51,21 @@ function resolvePathParams(endpoint, args) {
51
51
  });
52
52
  return { url, remainingArgs };
53
53
  }
54
- async function executeRequest(endpoint, method, args, auth, extraConfig) {
54
+ function resolveAuth(args, tokenParam, staticAuth) {
55
+ if (!tokenParam) return staticAuth;
56
+ const runtimeToken = args[tokenParam];
57
+ delete args[tokenParam];
58
+ if (typeof runtimeToken === "string" && runtimeToken.length > 0) {
59
+ return { type: "bearer", token: runtimeToken };
60
+ }
61
+ return staticAuth;
62
+ }
63
+ async function executeRequest(endpoint, method, args, auth, tokenParam, extraConfig) {
55
64
  try {
56
- const authHeaders = auth ? buildAuthHeaders(auth) : {};
57
- const { url, remainingArgs } = resolvePathParams(endpoint, args);
65
+ const mutableArgs = { ...args };
66
+ const resolvedAuth = resolveAuth(mutableArgs, tokenParam, auth);
67
+ const authHeaders = resolvedAuth ? buildAuthHeaders(resolvedAuth) : {};
68
+ const { url, remainingArgs } = resolvePathParams(endpoint, mutableArgs);
58
69
  const isQueryMethod = method === "GET" || method === "DELETE";
59
70
  const config = {
60
71
  url,
@@ -80,12 +91,12 @@ async function executeRequest(endpoint, method, args, auth, extraConfig) {
80
91
  }
81
92
  function httpToolAdapter(server, adapter) {
82
93
  if (adapter.inputSchema) {
83
- const { endpoint, method, auth, axiosConfig, inputSchema } = adapter;
94
+ const { endpoint, method, auth, tokenParam, axiosConfig, inputSchema } = adapter;
84
95
  toolAdapter(server, {
85
96
  name: adapter.name,
86
97
  config: { description: adapter.description, inputSchema },
87
98
  cb: (async (args) => {
88
- const { data, error } = await executeRequest(endpoint, method, args, auth, axiosConfig);
99
+ const { data, error } = await executeRequest(endpoint, method, args, auth, tokenParam, axiosConfig);
89
100
  return toolContentAdapter(data ?? {}, error);
90
101
  })
91
102
  });
@@ -99,6 +110,7 @@ function httpToolAdapter(server, adapter) {
99
110
  adapter.method,
100
111
  {},
101
112
  adapter.auth,
113
+ void 0,
102
114
  adapter.axiosConfig
103
115
  );
104
116
  return toolContentAdapter(data ?? {}, error);
@@ -121,13 +133,58 @@ function patchToolAdapter(server, config) {
121
133
  function deleteToolAdapter(server, config) {
122
134
  httpToolAdapter(server, { ...config, method: "DELETE" });
123
135
  }
136
+
137
+ // src/resourceAdapter.ts
138
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
139
+ function staticResourceAdapter(server, config) {
140
+ server.registerResource(
141
+ config.name,
142
+ config.uri,
143
+ {
144
+ title: config.title,
145
+ description: config.description,
146
+ mimeType: config.mimeType ?? "text/plain"
147
+ },
148
+ async (uri) => ({
149
+ contents: [
150
+ {
151
+ uri: uri.href,
152
+ text: await config.load(),
153
+ mimeType: config.mimeType ?? "text/plain"
154
+ }
155
+ ]
156
+ })
157
+ );
158
+ }
159
+ function dynamicResourceAdapter(server, config) {
160
+ server.registerResource(
161
+ config.name,
162
+ new ResourceTemplate(config.uriTemplate, { list: void 0 }),
163
+ {
164
+ title: config.title,
165
+ description: config.description,
166
+ mimeType: config.mimeType ?? "text/plain"
167
+ },
168
+ async (uri, params) => ({
169
+ contents: [
170
+ {
171
+ uri: uri.href,
172
+ text: await config.load(uri, params),
173
+ mimeType: config.mimeType ?? "text/plain"
174
+ }
175
+ ]
176
+ })
177
+ );
178
+ }
124
179
  export {
125
180
  deleteToolAdapter,
181
+ dynamicResourceAdapter,
126
182
  getToolAdapter,
127
183
  httpToolAdapter,
128
184
  patchToolAdapter,
129
185
  postToolAdapter,
130
186
  putToolAdapter,
187
+ staticResourceAdapter,
131
188
  toolAdapter,
132
189
  toolContentAdapter
133
190
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jalpp/mcp-adapter",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Adapter utilities for registering MCP tools with full TypeScript type safety — supports typed callbacks and automatic HTTP endpoint bridging with auth",
5
5
  "author": "jalpp",
6
6
  "license": "MIT",
@@ -40,21 +40,29 @@
40
40
  "scripts": {
41
41
  "build": "tsup",
42
42
  "dev": "tsup --watch",
43
- "prepublishOnly": "npm run build"
43
+ "prepublishOnly": "npm run build",
44
+ "version:patch": "npm version patch",
45
+ "version:minor": "npm version minor",
46
+ "version:major": "npm version major",
47
+ "release": "npm run build && npm publish --access public",
48
+ "release:dry": "npm run build && npm publish --dry-run",
49
+ "release:patch": "npm version patch && npm publish --access public",
50
+ "release:minor": "npm version minor && npm publish --access public",
51
+ "release:major": "npm version major && npm publish --access public"
44
52
  },
45
53
  "peerDependencies": {
46
54
  "@modelcontextprotocol/sdk": ">=1.0.0",
47
- "zod": ">=3.0.0",
48
- "axios": ">=1.0.0"
55
+ "axios": ">=1.0.0",
56
+ "zod": ">=3.0.0"
49
57
  },
50
58
  "devDependencies": {
51
59
  "@modelcontextprotocol/sdk": ">=1.0.0",
52
- "@types/node": "^20.0.0",
60
+ "@types/node": "^25.4.0",
53
61
  "tsup": "^8.0.0",
54
62
  "typescript": "^5.0.0",
55
63
  "zod": ">=3.0.0"
56
64
  },
57
65
  "dependencies": {
58
- "axios": "^1.13.6"
66
+ "axios": ">=1.0.0"
59
67
  }
60
68
  }