@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 +145 -48
- package/dist/index.d.ts +206 -11
- package/dist/index.js +62 -5
- package/package.json +14 -6
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
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
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-
|
|
39
|
-
description: "Fetch a
|
|
40
|
-
endpoint: "https://api.example.com/
|
|
49
|
+
name: "get-user",
|
|
50
|
+
description: "Fetch a user by ID",
|
|
51
|
+
endpoint: "https://api.example.com/users/:userId",
|
|
41
52
|
inputSchema: {
|
|
42
|
-
|
|
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/
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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: "
|
|
165
|
-
description: "
|
|
166
|
-
endpoint: "https://api.example.com/
|
|
181
|
+
name: "search-products",
|
|
182
|
+
description: "Search the product catalogue",
|
|
183
|
+
endpoint: "https://api.example.com/products/search",
|
|
167
184
|
method: "POST",
|
|
168
|
-
inputSchema: {
|
|
169
|
-
auth: { type: "
|
|
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: "
|
|
204
|
+
name: "summarize-report",
|
|
186
205
|
config: {
|
|
187
|
-
description: "
|
|
206
|
+
description: "Fetch and summarize a sales report",
|
|
188
207
|
inputSchema: {
|
|
189
|
-
|
|
190
|
-
|
|
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 ({
|
|
195
|
-
const { data, error } = await
|
|
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
|
|
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
57
|
-
const
|
|
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.
|
|
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
|
-
"
|
|
48
|
-
"
|
|
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": "^
|
|
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": "
|
|
66
|
+
"axios": ">=1.0.0"
|
|
59
67
|
}
|
|
60
68
|
}
|