vite-plugin-mcp-builder 0.0.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 +166 -0
- package/dist/define.d.ts +22 -0
- package/dist/define.js +46 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +100 -0
- package/package.json +55 -0
- package/src-dev/createServer.js +10 -0
- package/src-dev/devHandler.js +33 -0
- package/src-dev/main.js +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Vite Plugin: MCP Build
|
|
2
|
+
|
|
3
|
+
This Vite plugin helps develop and build Model Context Protocol (MCP) handlers.
|
|
4
|
+
|
|
5
|
+
Key features:
|
|
6
|
+
|
|
7
|
+
- Development server that mounts an MCP handler under `/mcp`.
|
|
8
|
+
- Production build that produces a single bundle at `dist/app.js`.
|
|
9
|
+
- Scans configured directories for `*.tool`, `*.prompt` and `*.resource` files to include in the bundle.
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The plugin scans the project files defined by the `dirs` option and exposes a development endpoint at `/mcp` that serves those definitions dynamically. For production it bundles the detected definitions into a single output file `dist/app.js`.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Install with your package manager:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yarn add -D vite-plugin-mcp-build
|
|
21
|
+
# or
|
|
22
|
+
npm install -D vite-plugin-mcp-build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage (example `vite.config.ts`)
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { defineConfig } from 'vite'
|
|
29
|
+
import mcpPlugin from 'vite-plugin-mcp-build'
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
plugins: [
|
|
33
|
+
mcpPlugin({
|
|
34
|
+
// optional plugin properties shown below
|
|
35
|
+
server: './src/createServer.js',
|
|
36
|
+
main: './src/main.js',
|
|
37
|
+
dirs: [
|
|
38
|
+
{ dir: './src/calculadora' }
|
|
39
|
+
]
|
|
40
|
+
})
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Plugin options
|
|
46
|
+
|
|
47
|
+
All options are optional. Defaults are shown where applicable.
|
|
48
|
+
|
|
49
|
+
- `root?: string` — Project root used to resolve relative paths. Defaults to `process.cwd()`.
|
|
50
|
+
- `cacheDir?: string` — Directory where generated helper files are written (e.g. `createInstance.js`). Default: `.mcp`.
|
|
51
|
+
- `server?: string` — Path to the server factory module used in production. Default: internal `src-dev/createServer.js` (resolved relative to `root`).
|
|
52
|
+
- `devHandler?: string` — Path to the development handler module used by the Vite dev server. Default: internal `src-dev/devHandler.js` (resolved relative to `root`).
|
|
53
|
+
- `main?: string` — The main production entry module used for SSR build. Default: internal `src-dev/main.js` (resolved relative to `root`).
|
|
54
|
+
- `include?: string[]` — Glob patterns used to match tool/prompt/resource files inside each `dirs` entry. Default patterns (the plugin supports both `.ts` and `.js`):
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
[
|
|
58
|
+
"**/*.tool.ts",
|
|
59
|
+
"**/*.prompt.ts",
|
|
60
|
+
"**/*.resource.ts",
|
|
61
|
+
"**/*.tool.js",
|
|
62
|
+
"**/*.prompt.js",
|
|
63
|
+
"**/*.resource.js"
|
|
64
|
+
]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Note: the original source contained a typo (`promt`). The plugin's intended pattern is `prompt`.
|
|
68
|
+
|
|
69
|
+
- `dirs?: { dir: string; include?: string[]; skip?: boolean }[]` — Array of directories to scan. Each entry:
|
|
70
|
+
- `dir` (required): directory path (relative to `root`) to scan.
|
|
71
|
+
- `include` (optional): per-dir globs overriding the global `include` list.
|
|
72
|
+
- `skip` (optional): if `true` the directory is ignored.
|
|
73
|
+
|
|
74
|
+
Default: `[{ dir: 'src', include: [], skip: false }]` — the plugin will scan `src` by default.
|
|
75
|
+
|
|
76
|
+
## Development mode
|
|
77
|
+
|
|
78
|
+
Run Vite in dev mode (e.g. `yarn dev`). The plugin configures the dev server and mounts the MCP handler under `/mcp` (the path is registered by the plugin's middleware).
|
|
79
|
+
|
|
80
|
+
By default Vite's dev server will include the plugin's middleware under the `/mcp` route. Example local URL:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
http://localhost:5173/mcp
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Register this URL in your agent during development, e.g.:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"servers": {
|
|
91
|
+
"my-mcp-dev": {
|
|
92
|
+
"type": "http",
|
|
93
|
+
"url": "http://localhost:5173/mcp"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Production build
|
|
100
|
+
|
|
101
|
+
Run your normal Vite build (e.g. `yarn build`). The plugin configures the build to produce a single SSR entry output named `app.js` (written under the `dist` directory by default). That file is suitable to load in a production process that creates the server instance and starts transports.
|
|
102
|
+
|
|
103
|
+
Example `main.js` (production runner):
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
107
|
+
import createInstance from "@mcp/createInstance.js";
|
|
108
|
+
|
|
109
|
+
const server = await createInstance();
|
|
110
|
+
const transport = new StdioServerTransport();
|
|
111
|
+
await server.connect(transport);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Defining tools, prompts and resources
|
|
115
|
+
|
|
116
|
+
Tools, prompts and resources are defined with the helper registrations (see `src/define.ts`). Typical usage registers a tool with a title, description, input schema and an implementation function. Example (TypeScript):
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { z } from 'zod';
|
|
120
|
+
import { defineRegisterTool } from '@mcp/define';
|
|
121
|
+
|
|
122
|
+
export default defineRegisterTool(
|
|
123
|
+
'divide',
|
|
124
|
+
{
|
|
125
|
+
title: 'Divide two numbers',
|
|
126
|
+
description: 'Divides two given numbers.',
|
|
127
|
+
inputSchema: {
|
|
128
|
+
number1: z.number(),
|
|
129
|
+
number2: z.number(),
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
async ({ number1, number2 }: any) => {
|
|
133
|
+
if (typeof number1 !== 'number' || typeof number2 !== 'number') {
|
|
134
|
+
throw new Error('Parameters must be numbers.');
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: 'text', text: String(number1 / number2) }]
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The registration helpers (`defineRegisterTool`, `defineRegisterPrompt`, `defineRegisterResource`) allow describing handlers without requiring a server instance at definition time.
|
|
144
|
+
|
|
145
|
+
## Example project layout
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
packages/example/src/
|
|
149
|
+
server.ts
|
|
150
|
+
calculadora/
|
|
151
|
+
dividir.tool.ts
|
|
152
|
+
multiplicar.tool.ts
|
|
153
|
+
nombre.resource.ts
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
With the configuration above, the plugin will find `dividir.tool.ts`, `multiplicar.tool.ts` and `nombre.resource.ts` and include them in the production bundle.
|
|
157
|
+
|
|
158
|
+
## Troubleshooting
|
|
159
|
+
|
|
160
|
+
- If files are not detected, verify your `dirs` entries and `include` globs, and ensure file extensions (`.ts`/`.js`) match your build.
|
|
161
|
+
- Inspect Vite's console output for plugin scan and build messages.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
If you want, I can also add concrete type signatures for the `defineRegister*` helpers and a small example that consumes `dist/app.js` in a production process.
|
|
166
|
+
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PromptCallback, McpServer, ResourceMetadata, ReadResourceCallback, ResourceTemplate, ReadResourceTemplateCallback, ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { ZodRawShapeCompat, AnySchema } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
3
|
+
import { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
|
|
5
|
+
type PromptArgsRawShape = ZodRawShapeCompat;
|
|
6
|
+
declare const defineRegisterTool: <OutputArgs extends ZodRawShapeCompat | AnySchema, InputArgs extends undefined | ZodRawShapeCompat | AnySchema = undefined>(name: string, config: {
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
inputSchema?: InputArgs;
|
|
10
|
+
outputSchema?: OutputArgs;
|
|
11
|
+
annotations?: ToolAnnotations;
|
|
12
|
+
_meta?: Record<string, unknown>;
|
|
13
|
+
}, cb: ToolCallback<InputArgs>) => (server: McpServer) => void;
|
|
14
|
+
declare const defineRegisterPrompt: <Args extends PromptArgsRawShape>(name: string, config: {
|
|
15
|
+
title?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
argsSchema?: Args;
|
|
18
|
+
}, cb: PromptCallback<Args>) => (server: McpServer) => void;
|
|
19
|
+
declare const defineRegisterResource: (name: string, uriOrTemplate: string, config: ResourceMetadata, readCallback: ReadResourceCallback) => (server: McpServer) => void;
|
|
20
|
+
declare const defineRegisterResourceV2: (name: string, uriOrTemplate: ResourceTemplate, config: ResourceMetadata, readCallback: ReadResourceTemplateCallback) => (server: McpServer) => void;
|
|
21
|
+
|
|
22
|
+
export { defineRegisterPrompt, defineRegisterResource, defineRegisterResourceV2, defineRegisterTool };
|
package/dist/define.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const defineRegisterTool = (name, config, cb) => {
|
|
2
|
+
return (server) => {
|
|
3
|
+
try {
|
|
4
|
+
console.log("RegisterTool:", name);
|
|
5
|
+
server.registerTool(name, config, cb);
|
|
6
|
+
} catch (error) {
|
|
7
|
+
console.error("RegisterTool:", error);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
const defineRegisterPrompt = (name, config, cb) => {
|
|
12
|
+
return (server) => {
|
|
13
|
+
try {
|
|
14
|
+
console.log("RegisterPrompt:", name);
|
|
15
|
+
server.registerPrompt(name, config, cb);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.error("RegisterPrompt:", error);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
const defineRegisterResource = (name, uriOrTemplate, config, readCallback) => {
|
|
22
|
+
return (server) => {
|
|
23
|
+
try {
|
|
24
|
+
console.log("RegisterResource:", name);
|
|
25
|
+
server.registerResource(name, uriOrTemplate, config, readCallback);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error("RegisterResource:", error);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const defineRegisterResourceV2 = (name, uriOrTemplate, config, readCallback) => {
|
|
32
|
+
return (server) => {
|
|
33
|
+
try {
|
|
34
|
+
console.log("RegisterResource:", name);
|
|
35
|
+
server.registerResource(name, uriOrTemplate, config, readCallback);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error("RegisterResourceV2:", error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export {
|
|
42
|
+
defineRegisterPrompt,
|
|
43
|
+
defineRegisterResource,
|
|
44
|
+
defineRegisterResourceV2,
|
|
45
|
+
defineRegisterTool
|
|
46
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
type PluginOpts = {
|
|
4
|
+
root?: string;
|
|
5
|
+
cacheDir?: string;
|
|
6
|
+
server?: string;
|
|
7
|
+
devHandler?: string;
|
|
8
|
+
main?: string;
|
|
9
|
+
include?: string[];
|
|
10
|
+
dirs?: {
|
|
11
|
+
dir: string;
|
|
12
|
+
include?: string[];
|
|
13
|
+
skip?: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
declare const mcpPlugin: (opts: PluginOpts) => Plugin[];
|
|
18
|
+
|
|
19
|
+
export { mcpPlugin as default, mcpPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { assertConfig } from "./model";
|
|
3
|
+
import { writeCreateInstance } from "./createInstanceFile";
|
|
4
|
+
const mcpPlugin = (opts) => {
|
|
5
|
+
const conf = assertConfig(opts);
|
|
6
|
+
writeCreateInstance(conf);
|
|
7
|
+
const isReload = (file) => {
|
|
8
|
+
return conf.hmrDirs.find((it) => file.startsWith(it));
|
|
9
|
+
};
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
name: "vite-plugin-mcp-alias",
|
|
13
|
+
enforce: "pre",
|
|
14
|
+
config: () => {
|
|
15
|
+
return {
|
|
16
|
+
resolve: {
|
|
17
|
+
alias: {
|
|
18
|
+
[`${conf.moduleId}/main.js`]: conf.mainFile,
|
|
19
|
+
[`${conf.moduleId}/createServer.js`]: conf.serverFile,
|
|
20
|
+
[`${conf.moduleId}/createInstance.js`]: conf.createInstanceFile
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "vite-plugin-mcp-server",
|
|
28
|
+
enforce: "pre",
|
|
29
|
+
apply: "serve",
|
|
30
|
+
config: () => {
|
|
31
|
+
return {
|
|
32
|
+
appType: "custom",
|
|
33
|
+
server: {
|
|
34
|
+
cors: true
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
configureServer: async (devServer) => {
|
|
39
|
+
const {
|
|
40
|
+
//
|
|
41
|
+
watcher,
|
|
42
|
+
restart,
|
|
43
|
+
middlewares,
|
|
44
|
+
ssrLoadModule,
|
|
45
|
+
ssrFixStacktrace
|
|
46
|
+
} = devServer;
|
|
47
|
+
var appServer = express();
|
|
48
|
+
const onReload = async (file) => {
|
|
49
|
+
if (isReload(file)) {
|
|
50
|
+
writeCreateInstance(conf);
|
|
51
|
+
restart();
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
watcher.on("all", (_, file) => onReload(file));
|
|
55
|
+
console.log("Load devHandlerFile!");
|
|
56
|
+
const { createHandler } = await ssrLoadModule(conf.devHandlerFile, {
|
|
57
|
+
fixStacktrace: true
|
|
58
|
+
});
|
|
59
|
+
const handler = await createHandler({});
|
|
60
|
+
appServer.use("/", async (req, res, next) => {
|
|
61
|
+
try {
|
|
62
|
+
handler(req, res, next);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
ssrFixStacktrace(error);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
next(error);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
middlewares.use("/mcp", appServer);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "vite-plugin-mcp-build",
|
|
74
|
+
enforce: "pre",
|
|
75
|
+
config: () => {
|
|
76
|
+
return {
|
|
77
|
+
appType: "custom",
|
|
78
|
+
target: "esnext",
|
|
79
|
+
emptyOutDir: true,
|
|
80
|
+
build: {
|
|
81
|
+
ssr: conf.mainFile,
|
|
82
|
+
rollupOptions: {
|
|
83
|
+
output: {
|
|
84
|
+
format: "es",
|
|
85
|
+
entryFileNames: "app.js",
|
|
86
|
+
chunkFileNames: "bin/[name].js",
|
|
87
|
+
assetFileNames: "assets/[name].[ext]"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
};
|
|
96
|
+
var src_default = mcpPlugin;
|
|
97
|
+
export {
|
|
98
|
+
src_default as default,
|
|
99
|
+
mcpPlugin
|
|
100
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-mcp-builder",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Vite plugin to build and serve MCP (Model Context Protocol) servers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./define": {
|
|
13
|
+
"import": "./dist/define.js",
|
|
14
|
+
"types": "./dist/define.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src-dev",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"prepublish": "yarn build",
|
|
25
|
+
"prepack": "yarn build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"vite",
|
|
29
|
+
"plugin",
|
|
30
|
+
"mcp",
|
|
31
|
+
"model-context-protocol",
|
|
32
|
+
"build-tool"
|
|
33
|
+
],
|
|
34
|
+
"author": "Willyams Yujra <yracnet@gmail.com>",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"homepage": "https://github.com/yracnet/vite-plugin-mcp-builder",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/yracnet/vite-plugin-mcp-builder"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
43
|
+
"vite": "^3 || ^4 || ^5 || ^6 || ^7",
|
|
44
|
+
"glob": "^13.0.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^24.10.1",
|
|
49
|
+
"glob": "^13.0.1",
|
|
50
|
+
"globals": "^16.5.0",
|
|
51
|
+
"tsup": "^8.5.1",
|
|
52
|
+
"typescript": "~5.9.3",
|
|
53
|
+
"vite": "^7.2.4"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import express from "express"
|
|
2
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
3
|
+
import createInstance from "@mcp/createInstance.js"
|
|
4
|
+
import cors from 'cors';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
export const createHandler = async ({
|
|
8
|
+
stateless = true
|
|
9
|
+
}) => {
|
|
10
|
+
const handler = express();
|
|
11
|
+
handler.use(express.json());
|
|
12
|
+
handler.use(cors({
|
|
13
|
+
origin: '*',
|
|
14
|
+
methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
|
|
15
|
+
allowedHeaders: ['Content-Type', 'mcp-session-id', 'Last-Event-ID', 'mcp-protocol-version'],
|
|
16
|
+
exposedHeaders: ['mcp-session-id', 'mcp-protocol-version']
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const transport = new StreamableHTTPServerTransport({
|
|
20
|
+
sessionIdGenerator: stateless ? undefined : randomUUID,
|
|
21
|
+
retryInterval: 2000,
|
|
22
|
+
});
|
|
23
|
+
const server = await createInstance();
|
|
24
|
+
server.connect(transport);
|
|
25
|
+
|
|
26
|
+
handler.all('/', async (req, res) => {
|
|
27
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
28
|
+
console.log(`\n${req.method} ${req.originalUrl}`, req.body, sessionId);
|
|
29
|
+
await transport.handleRequest(req, res, req.body);
|
|
30
|
+
});
|
|
31
|
+
return handler;
|
|
32
|
+
}
|
|
33
|
+
|
package/src-dev/main.js
ADDED