@jalpp/mcp-adapter 0.2.0 → 0.3.1

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.
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,41 +14,203 @@ npm install @jalpp/mcp-adapter
14
14
  npm install @modelcontextprotocol/sdk zod axios
15
15
  ```
16
16
 
17
+ ---
18
+
17
19
  ## Adapters
18
20
 
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
+
19
36
  | Adapter | Description |
20
37
  |---------|-------------|
21
- | `toolAdapter` | Register a tool with a typed callback |
22
- | `toolContentAdapter` | Normalize a result into a `CallToolResult` |
23
- | `httpToolAdapter` | Register an HTTP endpoint directly as a tool |
38
+ | `staticResourceAdapter` | Registers a static MCP resource with a fixed URI |
39
+ | `dynamicResourceAdapter` | Registers a dynamic MCP resource with a URI template |
40
+
41
+ ---
42
+
43
+ ## Path Variables
44
+
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.
46
+
47
+ ```ts
48
+ getToolAdapter(server, {
49
+ name: "get-user",
50
+ description: "Fetch a user by ID",
51
+ endpoint: "https://api.example.com/users/:userId",
52
+ inputSchema: {
53
+ userId: z.string().describe("User ID"),
54
+ expand: z.string().optional().describe("Optional fields to expand"),
55
+ },
56
+ auth: { type: "bearer", token: process.env.API_TOKEN! },
57
+ });
58
+ // Registers get-user tool which calls GET https://api.example.com/users/abc123?expand=profile
59
+ ```
60
+
61
+ ---
62
+
63
+ ## `getToolAdapter`
64
+
65
+ Registers a GET endpoint as an MCP tool. Remaining args (after path variable interpolation) are sent as **query parameters**.
66
+
67
+ ```ts
68
+ import { getToolAdapter } from "@jalpp/mcp-adapter";
69
+ import z from "zod";
70
+
71
+ getToolAdapter(server, {
72
+ name: "get-user",
73
+ description: "Fetch a user by ID",
74
+ endpoint: "https://api.example.com/users/:userId",
75
+ inputSchema: {
76
+ userId: z.string().describe("User ID"),
77
+ expand: z.string().optional().describe("Comma-separated fields to expand"),
78
+ },
79
+ auth: { type: "bearer", token: process.env.API_TOKEN! },
80
+ });
81
+ // Registers get-user tool which calls GET https://api.example.com/users/abc123?expand=profile
82
+ ```
83
+
84
+ ---
85
+
86
+ ## `postToolAdapter`
87
+
88
+ Registers a POST endpoint as an MCP tool. Remaining args (after path variable interpolation) are sent as the **JSON request body**.
89
+
90
+ ```ts
91
+ import { postToolAdapter } from "@jalpp/mcp-adapter";
92
+
93
+ postToolAdapter(server, {
94
+ name: "create-post",
95
+ description: "Create a new post for a user",
96
+ endpoint: "https://api.example.com/users/:userId/posts",
97
+ inputSchema: {
98
+ userId: z.string().describe("User ID"),
99
+ title: z.string().describe("Post title"),
100
+ body: z.string().describe("Post body"),
101
+ },
102
+ auth: { type: "bearer", token: process.env.API_TOKEN! },
103
+ });
104
+ // Registers create-post tool which calls POST https://api.example.com/users/abc123/posts { title, body }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## `putToolAdapter`
110
+
111
+ Registers a PUT endpoint as an MCP tool. Remaining args are sent as the **JSON request body**.
112
+
113
+ ```ts
114
+ import { putToolAdapter } from "@jalpp/mcp-adapter";
115
+
116
+ putToolAdapter(server, {
117
+ name: "update-user",
118
+ description: "Replace a user record",
119
+ endpoint: "https://api.example.com/users/:userId",
120
+ inputSchema: {
121
+ userId: z.string(),
122
+ name: z.string(),
123
+ email: z.string(),
124
+ },
125
+ auth: { type: "bearer", token: process.env.API_TOKEN! },
126
+ });
127
+ // Registers update-user tool which calls PUT https://api.example.com/users/abc123 { name, email }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## `patchToolAdapter`
133
+
134
+ Registers a PATCH endpoint as an MCP tool. Remaining args are sent as the **JSON request body**.
135
+
136
+ ```ts
137
+ import { patchToolAdapter } from "@jalpp/mcp-adapter";
138
+
139
+ patchToolAdapter(server, {
140
+ name: "update-post-title",
141
+ description: "Partially update a post",
142
+ endpoint: "https://api.example.com/posts/:postId",
143
+ inputSchema: {
144
+ postId: z.string(),
145
+ title: z.string(),
146
+ },
147
+ auth: { type: "bearer", token: process.env.API_TOKEN! },
148
+ });
149
+ // Registers update-post-title tool which calls PATCH https://api.example.com/posts/xyz789 { title }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## `deleteToolAdapter`
155
+
156
+ Registers a DELETE endpoint as an MCP tool. Remaining args (after path variable interpolation) are sent as **query parameters**.
157
+
158
+ ```ts
159
+ import { deleteToolAdapter } from "@jalpp/mcp-adapter";
160
+
161
+ deleteToolAdapter(server, {
162
+ name: "delete-post",
163
+ description: "Delete a post by ID",
164
+ endpoint: "https://api.example.com/posts/:postId",
165
+ inputSchema: { postId: z.string() },
166
+ auth: { type: "bearer", token: process.env.API_TOKEN! },
167
+ });
168
+ // Registers delete-post tool which calls DELETE https://api.example.com/posts/xyz789
169
+ ```
170
+
171
+ ---
172
+
173
+ ## `httpToolAdapter`
174
+
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.
176
+
177
+ ```ts
178
+ import { httpToolAdapter } from "@jalpp/mcp-adapter";
179
+
180
+ httpToolAdapter(server, {
181
+ name: "search-products",
182
+ description: "Search the product catalogue",
183
+ endpoint: "https://api.example.com/products/search",
184
+ method: "POST",
185
+ inputSchema: { query: z.string(), limit: z.number().optional() },
186
+ auth: { type: "apikey", header: "X-API-Key", key: process.env.API_KEY! },
187
+ });
188
+ // Registers search-products tool which calls POST https://api.example.com/products/search { query, limit }
189
+ ```
24
190
 
25
191
  ---
26
192
 
27
193
  ## `toolAdapter`
28
194
 
29
- Registers a typed tool on an `McpServer`. Input schema types flow through to the callback automatically no manual type annotations needed.
195
+ Registers a tool with a fully custom typed callback. Use when you need data transformation, custom error handling, or logic that goes beyond a single HTTP call.
30
196
 
31
197
  **With input schema:**
32
198
 
33
199
  ```ts
34
200
  import { toolAdapter, toolContentAdapter } from "@jalpp/mcp-adapter";
35
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
36
201
  import z from "zod";
37
202
 
38
- const server = new McpServer({ name: "my-server", version: "1.0.0" });
39
-
40
203
  toolAdapter(server, {
41
- name: "get-analysis",
204
+ name: "summarize-report",
42
205
  config: {
43
- description: "Analyze a chess position",
206
+ description: "Fetch and summarize a sales report",
44
207
  inputSchema: {
45
- fen: z.string().describe("FEN string"),
46
- depth: z.number().min(1).max(20),
208
+ reportId: z.string().describe("Report ID"),
209
+ format: z.enum(["short", "detailed"]).default("short"),
47
210
  },
48
- annotations: { openWorldHint: true },
49
211
  },
50
- cb: async ({ fen, depth }) => {
51
- const { data, error } = await myService.analyze(fen, depth);
212
+ cb: async ({ reportId, format }) => {
213
+ const { data, error } = await reportService.get(reportId, format);
52
214
  return toolContentAdapter(data ?? {}, error);
53
215
  },
54
216
  });
@@ -58,12 +220,10 @@ toolAdapter(server, {
58
220
 
59
221
  ```ts
60
222
  toolAdapter(server, {
61
- name: "get-status",
62
- config: {
63
- description: "Returns current server status",
64
- },
223
+ name: "get-server-status",
224
+ config: { description: "Returns current server status" },
65
225
  cb: async () => {
66
- const { data, error } = await myService.getStatus();
226
+ const { data, error } = await statusService.get();
67
227
  return toolContentAdapter(data ?? {}, error);
68
228
  },
69
229
  });
@@ -76,105 +236,117 @@ toolAdapter(server, {
76
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.
77
237
 
78
238
  ```ts
79
- import { toolContentAdapter } from "@jalpp/mcp-adapter";
80
-
81
239
  return toolContentAdapter(data ?? {}, error);
82
240
  ```
83
241
 
84
242
  | Argument | Type | Description |
85
243
  |----------|------|-------------|
86
244
  | `data` | `object` | Successful result payload |
87
- | `error` | `string \| undefined` | Error message — takes priority over `data` when present |
245
+ | `error` | `string \| undefined` | Error message — takes priority over `data` |
88
246
 
89
247
  ---
90
248
 
91
- ## `httpToolAdapter`
249
+ ## `staticResourceAdapter`
92
250
 
93
- Registers an HTTP endpoint directly as an MCP tool. Handles the full request lifecycle auth headers, query params vs request body, and error mapping so you only need to declare the tool metadata and schema.
94
-
95
- - **GET** requests map input args to **query parameters**
96
- - **POST / PUT / PATCH / DELETE** map input args to the **JSON request body**
97
-
98
- **With input schema:**
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).
99
252
 
100
253
  ```ts
101
- import { httpToolAdapter } from "@jalpp/mcp-adapter";
102
- import z from "zod";
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
+ });
103
265
 
104
- httpToolAdapter(server, {
105
- name: "get-analysis",
106
- description: "Fetch position analysis from external API",
107
- endpoint: "https://api.example.com/analyze",
108
- method: "POST",
109
- inputSchema: {
110
- fen: z.string().describe("FEN string"),
111
- depth: z.number().min(1).max(20),
112
- },
113
- auth: {
114
- type: "bearer",
115
- token: process.env.API_TOKEN!,
116
- },
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"),
117
274
  });
118
275
  ```
119
276
 
120
- **Without input schema:**
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.
121
282
 
122
283
  ```ts
123
- httpToolAdapter(server, {
124
- name: "get-server-status",
125
- description: "Fetch API health status",
126
- endpoint: "https://api.example.com/status",
127
- method: "GET",
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),
128
314
  });
129
315
  ```
130
316
 
131
- ### Authentication
317
+ ---
132
318
 
133
- Three auth strategies are supported, all passed via the `auth` field:
319
+ ## Authentication
134
320
 
135
- **Bearer token** sends `Authorization: Bearer <token>`:
321
+ All HTTP tool adapters accept an optional `auth` field. Three strategies are supported:
136
322
 
323
+ **Bearer token** — `Authorization: Bearer <token>`:
137
324
  ```ts
138
- auth: {
139
- type: "bearer",
140
- token: process.env.API_TOKEN!,
141
- }
325
+ auth: { type: "bearer", token: process.env.API_TOKEN! }
142
326
  ```
143
327
 
144
- **API key** — sends the key in a custom header:
145
-
328
+ **API key** — custom header:
146
329
  ```ts
147
- auth: {
148
- type: "apikey",
149
- header: "X-API-Key",
150
- key: process.env.API_KEY!,
151
- }
330
+ auth: { type: "apikey", header: "X-API-Key", key: process.env.API_KEY! }
152
331
  ```
153
332
 
154
- **Basic auth** — sends `Authorization: Basic <base64(username:password)>`:
155
-
333
+ **Basic auth** — `Authorization: Basic <base64>`:
156
334
  ```ts
157
- auth: {
158
- type: "basic",
159
- username: "myuser",
160
- password: process.env.PASSWORD!,
161
- }
335
+ auth: { type: "basic", username: "user", password: process.env.PASSWORD! }
162
336
  ```
163
337
 
164
- ### Extra axios config
338
+ ---
339
+
340
+ ## Extra axios config
165
341
 
166
- Pass any additional [axios request config](https://axios-http.com/docs/req_config) via `axiosConfig`:
342
+ Pass any [axios request config](https://axios-http.com/docs/req_config) via `axiosConfig`:
167
343
 
168
344
  ```ts
169
- httpToolAdapter(server, {
345
+ getToolAdapter(server, {
170
346
  name: "get-data",
171
347
  description: "Fetch with custom timeout",
172
348
  endpoint: "https://api.example.com/data",
173
- method: "GET",
174
- axiosConfig: {
175
- timeout: 5000,
176
- headers: { "Accept-Language": "en" },
177
- },
349
+ axiosConfig: { timeout: 5000 },
178
350
  });
179
351
  ```
180
352
 
@@ -182,23 +354,26 @@ httpToolAdapter(server, {
182
354
 
183
355
  ## API Reference
184
356
 
185
- ### `toolAdapter(server, adapter)`
186
-
187
- | Overload | When to use |
188
- |----------|-------------|
189
- | `toolAdapter<T>(server, ToolInputAdapterWithSchema<T>)` | Tool that receives typed input args |
190
- | `toolAdapter(server, ToolInputAdapterWithoutSchema)` | Tool that takes no input |
357
+ ### Tool adapters
191
358
 
192
- ### `toolContentAdapter(data, error)`
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 |
193
368
 
194
- Returns a `CallToolResult` with a single `text` content block.
369
+ All HTTP adapters support optional `inputSchema` and `:paramName` path variables.
195
370
 
196
- ### `httpToolAdapter(server, adapter)`
371
+ ### Resource adapters
197
372
 
198
- | Overload | When to use |
199
- |----------|-------------|
200
- | `httpToolAdapter<T>(server, HttpToolAdapterWithSchema<T>)` | Endpoint that receives typed input args |
201
- | `httpToolAdapter(server, HttpToolAdapterWithoutSchema)` | Endpoint that takes no input |
373
+ | Function | Registers | URI style |
374
+ |----------|-----------|-----------|
375
+ | `staticResourceAdapter` | fixed-URI resource | `"scheme://path"` |
376
+ | `dynamicResourceAdapter` | templated resource | `"scheme://{param}/path"` |
202
377
 
203
378
  ### Auth types
204
379
 
package/dist/index.d.ts CHANGED
@@ -124,23 +124,23 @@ interface BasicAuth {
124
124
  username: string;
125
125
  password: string;
126
126
  }
127
- /**
128
- * Union of all supported authentication strategies.
129
- */
127
+ /** Union of all supported authentication strategies. */
130
128
  type HttpAuth = ApiKeyAuth | BearerAuth | BasicAuth;
131
129
  /**
132
130
  * Configuration for registering an HTTP endpoint as an MCP tool with an input schema.
133
- *
134
- * @template T - Zod raw shape inferred from `inputSchema`, typed through to the request.
131
+ * @template T - Zod raw shape inferred from `inputSchema`.
135
132
  */
136
133
  interface HttpToolAdapterWithSchema<T extends ZodRawShapeCompat> {
137
134
  /** Unique tool name in the MCP registry. */
138
135
  name: string;
139
136
  /** Description of the tool shown to the model. */
140
137
  description: string;
141
- /** Full URL of the API endpoint. */
138
+ /**
139
+ * Full URL of the API endpoint. Supports path variable templates using `:paramName` syntax.
140
+ * @example "https://api.example.com/users/:userId/posts"
141
+ */
142
142
  endpoint: string;
143
- /** HTTP method to use. GET maps args to query params; others map to request body. */
143
+ /** HTTP method to use. */
144
144
  method: HttpMethod;
145
145
  /** Zod shape defining the tool's input arguments. */
146
146
  inputSchema: T;
@@ -157,7 +157,10 @@ interface HttpToolAdapterWithoutSchema {
157
157
  name: string;
158
158
  /** Description of the tool shown to the model. */
159
159
  description: string;
160
- /** Full URL of the API endpoint. */
160
+ /**
161
+ * Full URL of the API endpoint. Supports path variable templates using `:paramName` syntax.
162
+ * @example "https://api.example.com/status"
163
+ */
161
164
  endpoint: string;
162
165
  /** HTTP method to use. */
163
166
  method: HttpMethod;
@@ -168,41 +171,334 @@ interface HttpToolAdapterWithoutSchema {
168
171
  axiosConfig?: AxiosRequestConfig;
169
172
  }
170
173
  /**
171
- * Registers an HTTP endpoint as an MCP tool with a typed input schema.
174
+ * Configuration specific to GET tool adapters.
175
+ * Input args are mapped to query parameters unless consumed by path variables.
176
+ * @template T - Zod raw shape inferred from `inputSchema`.
177
+ */
178
+ type GetToolAdapterConfig<T extends ZodRawShapeCompat> = Omit<HttpToolAdapterWithSchema<T>, "method">;
179
+ /**
180
+ * Configuration specific to GET tool adapters without input schema.
181
+ */
182
+ type GetToolAdapterConfigNoSchema = Omit<HttpToolAdapterWithoutSchema, "method">;
183
+ /**
184
+ * Configuration specific to POST tool adapters.
185
+ * Input args are sent as the JSON request body unless consumed by path variables.
186
+ * @template T - Zod raw shape inferred from `inputSchema`.
187
+ */
188
+ type PostToolAdapterConfig<T extends ZodRawShapeCompat> = Omit<HttpToolAdapterWithSchema<T>, "method">;
189
+ /**
190
+ * Configuration specific to POST tool adapters without input schema.
191
+ */
192
+ type PostToolAdapterConfigNoSchema = Omit<HttpToolAdapterWithoutSchema, "method">;
193
+ /**
194
+ * Configuration specific to PUT tool adapters.
195
+ * Input args are sent as the JSON request body unless consumed by path variables.
196
+ * @template T - Zod raw shape inferred from `inputSchema`.
197
+ */
198
+ type PutToolAdapterConfig<T extends ZodRawShapeCompat> = Omit<HttpToolAdapterWithSchema<T>, "method">;
199
+ /**
200
+ * Configuration specific to PATCH tool adapters.
201
+ * Input args are sent as the JSON request body unless consumed by path variables.
202
+ * @template T - Zod raw shape inferred from `inputSchema`.
203
+ */
204
+ type PatchToolAdapterConfig<T extends ZodRawShapeCompat> = Omit<HttpToolAdapterWithSchema<T>, "method">;
205
+ /**
206
+ * Configuration specific to DELETE tool adapters.
207
+ * Input args are sent as query parameters unless consumed by path variables.
208
+ * @template T - Zod raw shape inferred from `inputSchema`.
209
+ */
210
+ type DeleteToolAdapterConfig<T extends ZodRawShapeCompat> = Omit<HttpToolAdapterWithSchema<T>, "method">;
211
+ /**
212
+ * Core HTTP tool adapter. Registers any HTTP endpoint as an MCP tool.
172
213
  *
173
- * GET requests map input args to query parameters.
174
- * All other methods map input args to the JSON request body.
214
+ * Prefer the method-specific adapters (`getToolAdapter`, `postToolAdapter`, etc.)
215
+ * for cleaner, self-documenting code.
216
+ *
217
+ * - Path variables (`:paramName`) are interpolated from input args.
218
+ * - GET/DELETE: remaining args → query parameters.
219
+ * - POST/PUT/PATCH: remaining args → JSON request body.
175
220
  *
176
221
  * @template T - Zod raw shape inferred from `inputSchema`.
177
222
  * @param server - The `McpServer` instance to register the tool on.
178
- * @param adapter - HTTP tool configuration including endpoint, method, schema, and auth.
223
+ * @param adapter - HTTP tool configuration.
224
+ */
225
+ declare function httpToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, adapter: HttpToolAdapterWithSchema<T>): void;
226
+ declare function httpToolAdapter(server: McpServer, adapter: HttpToolAdapterWithoutSchema): void;
227
+ /**
228
+ * Registers a GET endpoint as an MCP tool.
229
+ *
230
+ * - Path variables (`:paramName`) are interpolated from input args.
231
+ * - Remaining args are sent as **query parameters**.
232
+ *
233
+ * @template T - Zod raw shape inferred from `inputSchema`.
234
+ * @param server - The `McpServer` instance to register the tool on.
235
+ * @param config - Tool configuration without `method`.
179
236
  *
180
237
  * @example
181
- * httpToolAdapter(server, {
182
- * name: "get-analysis",
183
- * description: "Fetch position analysis",
184
- * endpoint: "https://api.example.com/analyze",
185
- * method: "POST",
186
- * inputSchema: { fen: z.string(), depth: z.number() },
238
+ * getToolAdapter(server, {
239
+ * name: "get-user",
240
+ * description: "Fetch a user by ID",
241
+ * endpoint: "https://api.example.com/users/:userId",
242
+ * inputSchema: { userId: z.string(), expand: z.string().optional() },
187
243
  * auth: { type: "bearer", token: process.env.API_TOKEN! },
188
244
  * });
245
+ * // GET https://api.example.com/users/abc123?expand=profile
189
246
  */
190
- declare function httpToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, adapter: HttpToolAdapterWithSchema<T>): void;
247
+ declare function getToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: GetToolAdapterConfig<T>): void;
248
+ declare function getToolAdapter(server: McpServer, config: GetToolAdapterConfigNoSchema): void;
191
249
  /**
192
- * Registers an HTTP endpoint as an MCP tool with no input arguments.
250
+ * Registers a POST endpoint as an MCP tool.
193
251
  *
194
- * @param server - The `McpServer` instance to register the tool on.
195
- * @param adapter - HTTP tool configuration including endpoint, method, and auth.
252
+ * - Path variables (`:paramName`) are interpolated from input args.
253
+ * - Remaining args are sent as the **JSON request body**.
254
+ *
255
+ * @template T - Zod raw shape inferred from `inputSchema`.
256
+ * @param server - The `McpServer` instance to register the tool on.
257
+ * @param config - Tool configuration without `method`.
196
258
  *
197
259
  * @example
198
- * httpToolAdapter(server, {
199
- * name: "get-status",
200
- * description: "Fetch API status",
201
- * endpoint: "https://api.example.com/status",
202
- * method: "GET",
203
- * auth: { type: "apikey", header: "X-API-Key", key: process.env.API_KEY! },
260
+ * postToolAdapter(server, {
261
+ * name: "create-post",
262
+ * description: "Create a new post for a user",
263
+ * 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! },
204
266
  * });
267
+ * // POST https://api.example.com/users/abc123/posts { title, body }
205
268
  */
206
- declare function httpToolAdapter(server: McpServer, adapter: HttpToolAdapterWithoutSchema): void;
269
+ declare function postToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: PostToolAdapterConfig<T>): void;
270
+ declare function postToolAdapter(server: McpServer, config: PostToolAdapterConfigNoSchema): void;
271
+ /**
272
+ * Registers a PUT endpoint as an MCP tool.
273
+ *
274
+ * - Path variables (`:paramName`) are interpolated from input args.
275
+ * - Remaining args are sent as the **JSON request body**.
276
+ *
277
+ * @template T - Zod raw shape inferred from `inputSchema`.
278
+ * @param server - The `McpServer` instance to register the tool on.
279
+ * @param config - Tool configuration without `method`.
280
+ *
281
+ * @example
282
+ * putToolAdapter(server, {
283
+ * name: "update-user",
284
+ * description: "Replace a user record",
285
+ * 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! },
288
+ * });
289
+ * // PUT https://api.example.com/users/abc123 { name, email }
290
+ */
291
+ declare function putToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: PutToolAdapterConfig<T>): void;
292
+ /**
293
+ * Registers a PATCH endpoint as an MCP tool.
294
+ *
295
+ * - Path variables (`:paramName`) are interpolated from input args.
296
+ * - Remaining args are sent as the **JSON request body**.
297
+ *
298
+ * @template T - Zod raw shape inferred from `inputSchema`.
299
+ * @param server - The `McpServer` instance to register the tool on.
300
+ * @param config - Tool configuration without `method`.
301
+ *
302
+ * @example
303
+ * patchToolAdapter(server, {
304
+ * name: "update-post-title",
305
+ * description: "Partially update a post",
306
+ * endpoint: "https://api.example.com/posts/:postId",
307
+ * inputSchema: { postId: z.string(), title: z.string() },
308
+ * auth: { type: "bearer", token: process.env.API_TOKEN! },
309
+ * });
310
+ * // PATCH https://api.example.com/posts/xyz789 { title }
311
+ */
312
+ declare function patchToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: PatchToolAdapterConfig<T>): void;
313
+ /**
314
+ * Registers a DELETE endpoint as an MCP tool.
315
+ *
316
+ * - Path variables (`:paramName`) are interpolated from input args.
317
+ * - Remaining args are sent as **query parameters**.
318
+ *
319
+ * @template T - Zod raw shape inferred from `inputSchema`.
320
+ * @param server - The `McpServer` instance to register the tool on.
321
+ * @param config - Tool configuration without `method`.
322
+ *
323
+ * @example
324
+ * deleteToolAdapter(server, {
325
+ * name: "delete-post",
326
+ * description: "Delete a post by ID",
327
+ * endpoint: "https://api.example.com/posts/:postId",
328
+ * inputSchema: { postId: z.string() },
329
+ * auth: { type: "bearer", token: process.env.API_TOKEN! },
330
+ * });
331
+ * // DELETE https://api.example.com/posts/xyz789
332
+ */
333
+ declare function deleteToolAdapter<T extends ZodRawShapeCompat>(server: McpServer, config: DeleteToolAdapterConfig<T>): void;
334
+
335
+ /**
336
+ * Supported MIME types for resource content.
337
+ * Extend with additional types as needed.
338
+ */
339
+ type ResourceMimeType = "text/plain" | "text/html" | "text/markdown" | "application/json" | "application/xml" | (string & {});
340
+ /**
341
+ * Configuration for registering a static MCP resource.
342
+ *
343
+ * A static resource has a fixed URI with no variable parameters.
344
+ * Use this for resources whose content may change but whose identity does not
345
+ * (e.g. a config file, a status page, a knowledge base).
346
+ *
347
+ * @example
348
+ * staticResourceAdapter(server, {
349
+ * name: "server-config",
350
+ * uri: "config://server",
351
+ * title: "Server Configuration",
352
+ * description: "Current server configuration and environment settings",
353
+ * mimeType: "application/json",
354
+ * load: async () => JSON.stringify(await configService.get()),
355
+ * });
356
+ */
357
+ interface StaticResourceConfig {
358
+ /** Unique resource name used to identify the resource in the MCP registry. */
359
+ name: string;
360
+ /**
361
+ * The fixed URI that clients use to request this resource.
362
+ * @example "config://server"
363
+ * @example "docs://readme"
364
+ */
365
+ uri: string;
366
+ /** Human-readable display title shown in client UIs. */
367
+ title: string;
368
+ /** Description of what the resource contains, shown to the model. */
369
+ description: string;
370
+ /** MIME type of the resource content. Defaults to `"text/plain"`. */
371
+ mimeType?: ResourceMimeType;
372
+ /**
373
+ * Async function that returns the resource content as a string.
374
+ * Called each time a client requests the resource.
375
+ *
376
+ * @returns The resource content string.
377
+ *
378
+ * @example
379
+ * load: async () => JSON.stringify(await configService.get())
380
+ */
381
+ load: () => Promise<string> | string;
382
+ }
383
+ /**
384
+ * Configuration for registering a dynamic MCP resource with URI template parameters.
385
+ *
386
+ * A dynamic resource uses a URI template with `{paramName}` placeholders.
387
+ * The matched parameter values are passed to the `load` callback.
388
+ * Use this for resources identified by an ID or other variable (e.g. a user profile, an order record).
389
+ *
390
+ * @example
391
+ * dynamicResourceAdapter(server, {
392
+ * name: "user-profile",
393
+ * uriTemplate: "users://{userId}/profile",
394
+ * title: "User Profile",
395
+ * description: "Profile data for a given user",
396
+ * mimeType: "application/json",
397
+ * load: async (uri, { userId }) => JSON.stringify(await userService.getProfile(userId)),
398
+ * });
399
+ */
400
+ interface DynamicResourceConfig {
401
+ /** Unique resource name used to identify the resource in the MCP registry. */
402
+ name: string;
403
+ /**
404
+ * URI template string with `{paramName}` placeholders.
405
+ * @example "users://{userId}/profile"
406
+ * @example "orders://{orderId}/invoice"
407
+ */
408
+ uriTemplate: string;
409
+ /** human-readable display title shown in client UIs. */
410
+ title: string;
411
+ /** Description of what the resource contains, shown to the model. */
412
+ description: string;
413
+ /** MIME type of the resource content. Defaults to `"text/plain"`. */
414
+ mimeType?: ResourceMimeType;
415
+ /**
416
+ * Async function that returns the resource content as a string.
417
+ * Called each time a client requests the resource with a matching URI.
418
+ *
419
+ * @param uri - The full resolved URI of the request (as a `URL` object).
420
+ * @param params - Key-value map of extracted URI template parameters.
421
+ * @returns The resource content string.
422
+ *
423
+ * @example
424
+ * load: async (uri, { orderId }) => JSON.stringify(await orderService.getInvoice(orderId))
425
+ */
426
+ load: (uri: URL, params: Record<string, string>) => Promise<string> | string;
427
+ }
428
+ /**
429
+ * Registers a static MCP resource on an `McpServer`.
430
+ *
431
+ * Static resources have a fixed URI. The `load` callback is invoked on every
432
+ * client request and may return fresh data each time.
433
+ *
434
+ * @param server - The `McpServer` instance to register the resource on.
435
+ * @param config - Static resource configuration.
436
+ *
437
+ * @example
438
+ * // Expose a live system health report
439
+ * staticResourceAdapter(server, {
440
+ * name: "system-health",
441
+ * uri: "status://health",
442
+ * title: "System Health",
443
+ * description: "Live health status of all services",
444
+ * mimeType: "application/json",
445
+ * load: async () => JSON.stringify(await healthService.getReport()),
446
+ * });
447
+ *
448
+ * @example
449
+ * // Expose a static markdown documentation page
450
+ * staticResourceAdapter(server, {
451
+ * name: "api-docs",
452
+ * uri: "docs://api",
453
+ * title: "API Documentation",
454
+ * description: "REST API reference documentation",
455
+ * mimeType: "text/markdown",
456
+ * load: () => fs.readFileSync("./docs/api.md", "utf-8"),
457
+ * });
458
+ */
459
+ declare function staticResourceAdapter(server: McpServer, config: StaticResourceConfig): void;
460
+ /**
461
+ * Registers a dynamic MCP resource with a URI template on an `McpServer`.
462
+ *
463
+ * Dynamic resources use `{paramName}` placeholders in the URI template.
464
+ * Matched parameter values are extracted and passed to the `load` callback.
465
+ *
466
+ * @param server - The `McpServer` instance to register the resource on.
467
+ * @param config - Dynamic resource configuration.
468
+ *
469
+ * @example
470
+ * // Expose a user profile by ID
471
+ * dynamicResourceAdapter(server, {
472
+ * name: "user-profile",
473
+ * uriTemplate: "users://{userId}/profile",
474
+ * title: "User Profile",
475
+ * description: "Profile data for a specific user",
476
+ * mimeType: "application/json",
477
+ * load: async (uri, { userId }) => JSON.stringify(await userService.getProfile(userId)),
478
+ * });
479
+ *
480
+ * @example
481
+ * // Expose an order invoice by order ID
482
+ * dynamicResourceAdapter(server, {
483
+ * name: "order-invoice",
484
+ * uriTemplate: "orders://{orderId}/invoice",
485
+ * title: "Order Invoice",
486
+ * description: "Invoice details for a specific order",
487
+ * mimeType: "application/json",
488
+ * load: async (uri, { orderId }) => JSON.stringify(await orderService.getInvoice(orderId)),
489
+ * });
490
+ *
491
+ * @example
492
+ * // Expose a blog post by slug
493
+ * dynamicResourceAdapter(server, {
494
+ * name: "blog-post",
495
+ * uriTemplate: "blog://{slug}",
496
+ * title: "Blog Post",
497
+ * description: "Markdown content for a blog post",
498
+ * mimeType: "text/markdown",
499
+ * load: async (uri, { slug }) => await blogService.getPostMarkdown(slug),
500
+ * });
501
+ */
502
+ declare function dynamicResourceAdapter(server: McpServer, config: DynamicResourceConfig): void;
207
503
 
208
- export { type ApiKeyAuth, type BasicAuth, type BearerAuth, type HttpAuth, type HttpMethod, type HttpToolAdapterWithSchema, type HttpToolAdapterWithoutSchema, httpToolAdapter, toolAdapter, toolContentAdapter };
504
+ 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
@@ -39,18 +39,32 @@ function buildAuthHeaders(auth) {
39
39
  }
40
40
  }
41
41
  }
42
+ function resolvePathParams(endpoint, args) {
43
+ const remainingArgs = { ...args };
44
+ const url = endpoint.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, key) => {
45
+ if (key in remainingArgs) {
46
+ const value = remainingArgs[key];
47
+ delete remainingArgs[key];
48
+ return encodeURIComponent(String(value));
49
+ }
50
+ return `:${key}`;
51
+ });
52
+ return { url, remainingArgs };
53
+ }
42
54
  async function executeRequest(endpoint, method, args, auth, extraConfig) {
43
55
  try {
44
56
  const authHeaders = auth ? buildAuthHeaders(auth) : {};
57
+ const { url, remainingArgs } = resolvePathParams(endpoint, args);
58
+ const isQueryMethod = method === "GET" || method === "DELETE";
45
59
  const config = {
46
- url: endpoint,
60
+ url,
47
61
  method,
48
62
  headers: {
49
63
  "Content-Type": "application/json",
50
64
  ...authHeaders,
51
65
  ...extraConfig?.headers
52
66
  },
53
- ...method === "GET" ? { params: args } : { data: args },
67
+ ...isQueryMethod ? { params: remainingArgs } : { data: remainingArgs },
54
68
  ...extraConfig
55
69
  };
56
70
  const response = await axios(config);
@@ -69,10 +83,7 @@ function httpToolAdapter(server, adapter) {
69
83
  const { endpoint, method, auth, axiosConfig, inputSchema } = adapter;
70
84
  toolAdapter(server, {
71
85
  name: adapter.name,
72
- config: {
73
- description: adapter.description,
74
- inputSchema
75
- },
86
+ config: { description: adapter.description, inputSchema },
76
87
  cb: (async (args) => {
77
88
  const { data, error } = await executeRequest(endpoint, method, args, auth, axiosConfig);
78
89
  return toolContentAdapter(data ?? {}, error);
@@ -81,9 +92,7 @@ function httpToolAdapter(server, adapter) {
81
92
  } else {
82
93
  toolAdapter(server, {
83
94
  name: adapter.name,
84
- config: {
85
- description: adapter.description
86
- },
95
+ config: { description: adapter.description },
87
96
  cb: async () => {
88
97
  const { data, error } = await executeRequest(
89
98
  adapter.endpoint,
@@ -97,8 +106,73 @@ function httpToolAdapter(server, adapter) {
97
106
  });
98
107
  }
99
108
  }
109
+ function getToolAdapter(server, config) {
110
+ httpToolAdapter(server, { ...config, method: "GET" });
111
+ }
112
+ function postToolAdapter(server, config) {
113
+ httpToolAdapter(server, { ...config, method: "POST" });
114
+ }
115
+ function putToolAdapter(server, config) {
116
+ httpToolAdapter(server, { ...config, method: "PUT" });
117
+ }
118
+ function patchToolAdapter(server, config) {
119
+ httpToolAdapter(server, { ...config, method: "PATCH" });
120
+ }
121
+ function deleteToolAdapter(server, config) {
122
+ httpToolAdapter(server, { ...config, method: "DELETE" });
123
+ }
124
+
125
+ // src/resourceAdapter.ts
126
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
127
+ function staticResourceAdapter(server, config) {
128
+ server.registerResource(
129
+ config.name,
130
+ config.uri,
131
+ {
132
+ title: config.title,
133
+ description: config.description,
134
+ mimeType: config.mimeType ?? "text/plain"
135
+ },
136
+ async (uri) => ({
137
+ contents: [
138
+ {
139
+ uri: uri.href,
140
+ text: await config.load(),
141
+ mimeType: config.mimeType ?? "text/plain"
142
+ }
143
+ ]
144
+ })
145
+ );
146
+ }
147
+ function dynamicResourceAdapter(server, config) {
148
+ server.registerResource(
149
+ config.name,
150
+ new ResourceTemplate(config.uriTemplate, { list: void 0 }),
151
+ {
152
+ title: config.title,
153
+ description: config.description,
154
+ mimeType: config.mimeType ?? "text/plain"
155
+ },
156
+ async (uri, params) => ({
157
+ contents: [
158
+ {
159
+ uri: uri.href,
160
+ text: await config.load(uri, params),
161
+ mimeType: config.mimeType ?? "text/plain"
162
+ }
163
+ ]
164
+ })
165
+ );
166
+ }
100
167
  export {
168
+ deleteToolAdapter,
169
+ dynamicResourceAdapter,
170
+ getToolAdapter,
101
171
  httpToolAdapter,
172
+ patchToolAdapter,
173
+ postToolAdapter,
174
+ putToolAdapter,
175
+ staticResourceAdapter,
102
176
  toolAdapter,
103
177
  toolContentAdapter
104
178
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jalpp/mcp-adapter",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
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",
@@ -44,17 +44,17 @@
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@modelcontextprotocol/sdk": ">=1.0.0",
47
- "zod": ">=3.0.0",
48
- "axios": ">=1.0.0"
47
+ "axios": ">=1.0.0",
48
+ "zod": ">=3.0.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@modelcontextprotocol/sdk": ">=1.0.0",
52
- "@types/node": "^20.0.0",
52
+ "@types/node": "^25.4.0",
53
53
  "tsup": "^8.0.0",
54
54
  "typescript": "^5.0.0",
55
55
  "zod": ">=3.0.0"
56
56
  },
57
57
  "dependencies": {
58
- "axios": "^1.13.6"
58
+ "axios": ">=1.0.0"
59
59
  }
60
60
  }