hono-mcp 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # Hono MCP Server
2
+
3
+ 一个基于 [Hono](https://hono.dev/) 构建的 Model Context Protocol (MCP) 服务器,支持多种部署方式。
4
+
5
+ ## ✨ 特性
6
+
7
+ - 🎯 **装饰器模式**:使用 TypeScript 装饰器优雅地定义 MCP 工具
8
+ - 🚀 **多种部署方式**:支持 Vercel 部署和本地 CLI 运行
9
+ - 🔧 **统一命令接口**:通过 `executeCommand` 和 `help` 两个工具管理所有命令
10
+ - 📦 **即插即用**:通过 `npx` 快速启动,无需复杂配置
11
+ - 🛡️ **类型安全**:使用 TypeScript 和 Zod 进行类型检查和验证
12
+ - 🎨 **易于扩展**:添加新命令只需创建新的装饰器类
13
+
14
+ ## 📦 安装
15
+
16
+ ### 通过 npx 直接运行(推荐)
17
+
18
+ ```bash
19
+ npx hono-mcp
20
+ ```
21
+
22
+ ### 全局安装
23
+
24
+ ```bash
25
+ npm install -g hono-mcp
26
+ hono-mcp
27
+ ```
28
+
29
+ ### 作为项目依赖安装
30
+
31
+ ```bash
32
+ npm install hono-mcp
33
+ npx hono-mcp
34
+ ```
35
+
36
+ ## 🚀 使用方法
37
+
38
+ ### 命令行使用
39
+
40
+ ```bash
41
+ # 使用默认配置(端口 3000)
42
+ npx hono-mcp
43
+
44
+ # 指定端口
45
+ npx hono-mcp --port 8080
46
+
47
+ # 指定主机
48
+ npx hono-mcp --host localhost
49
+
50
+ # 指定环境
51
+ npx hono-mcp --env development
52
+
53
+ # 组合使用
54
+ npx hono-mcp --port 8080 --host localhost --env development
55
+
56
+ # 查看帮助
57
+ npx hono-mcp --help
58
+ ```
59
+
60
+ ### 环境变量
61
+
62
+ ```bash
63
+ # 设置端口
64
+ export PORT=8080
65
+ npx hono-mcp
66
+
67
+ # 设置主机
68
+ export HOST=localhost
69
+ npx hono-mcp
70
+
71
+ # 设置环境
72
+ export NODE_ENV=development
73
+ npx hono-mcp
74
+ ```
75
+
76
+ ## 🌐 部署方式
77
+
78
+ ### 方式 1:Vercel 部署
79
+
80
+ 1. **克隆项目**
81
+
82
+ ```bash
83
+ git clone https://github.com/Fromsko/hono-mcp-remote-server.git
84
+ cd hono-mcp-remote-server
85
+ ```
86
+
87
+ 2. **安装依赖**
88
+
89
+ ```bash
90
+ bun install
91
+ ```
92
+
93
+ 3. **本地开发**
94
+
95
+ ```bash
96
+ bun run dev:vercel
97
+ ```
98
+
99
+ 4. **部署到 Vercel**
100
+
101
+ ```bash
102
+ vc deploy
103
+ ```
104
+
105
+ 或者通过 Vercel Dashboard 连接 GitHub 仓库进行自动部署。
106
+
107
+ ### 方式 2:本地服务器
108
+
109
+ 1. **克隆项目**
110
+
111
+ ```bash
112
+ git clone https://github.com/Fromsko/hono-mcp-remote-server.git
113
+ cd hono-mcp-remote-server
114
+ ```
115
+
116
+ 2. **安装依赖**
117
+
118
+ ```bash
119
+ bun install
120
+ ```
121
+
122
+ 3. **构建项目**
123
+
124
+ ```bash
125
+ bun run build
126
+ ```
127
+
128
+ 4. **启动服务器**
129
+
130
+ ```bash
131
+ bun run start
132
+ ```
133
+
134
+ 或者使用 npx:
135
+
136
+ ```bash
137
+ npx hono-mcp --port 3000
138
+ ```
139
+
140
+ ## 📡 API 端点
141
+
142
+ - **GET `/`** - 服务器信息和可用工具列表
143
+ - **POST `/mcp/*`** - MCP 协议端点,用于工具执行
144
+ - **GET `/help`** - 查看所有可用命令的帮助信息
145
+ - **GET `/vscode`** - VS Code 设置示例
146
+
147
+ ## 🔧 开发
148
+
149
+ ### 本地开发
150
+
151
+ ```bash
152
+ # 安装依赖
153
+ bun install
154
+
155
+ # 开发模式(CLI)
156
+ bun run dev
157
+
158
+ # 开发模式(Vercel)
159
+ bun run dev:vercel
160
+
161
+ # 构建
162
+ bun run build
163
+
164
+ # 启动生产服务器
165
+ bun run start
166
+ ```
167
+
168
+ ### 添加新命令
169
+
170
+ 使用装饰器模式创建新命令非常简单:
171
+
172
+ ```typescript
173
+ import { Command, Param } from '../decorators/command.js'
174
+ import { z } from 'zod'
175
+ import type { CommandHandler } from '../decorators/command.js'
176
+
177
+ @Command('math.square', 'Calculate the square of a number')
178
+ export class SquareCommand implements CommandHandler {
179
+ @Param(z.number().describe('The number to square'))
180
+ number!: number
181
+
182
+ async execute() {
183
+ const result = this.number * this.number
184
+ return {
185
+ content: [{ type: 'text', text: `${this.number}² = ${result}` }],
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ 然后在 `src/index.ts` 中导入该命令:
192
+
193
+ ```typescript
194
+ import './commands/math.js'
195
+ import './commands/your-new-command.js'
196
+ ```
197
+
198
+ ## 📖 使用 MCP 服务器
199
+
200
+ ### 通过 executeCommand 工具
201
+
202
+ ```json
203
+ {
204
+ "type": "math.add",
205
+ "params": {
206
+ "a": 5,
207
+ "b": 3
208
+ }
209
+ }
210
+ ```
211
+
212
+ ### 通过 help 工具
213
+
214
+ 查看所有可用命令及其描述。
215
+
216
+ ## 🎯 可用命令
217
+
218
+ 当前支持以下数学运算命令:
219
+
220
+ - `math.add` - 加法运算
221
+ - `math.subtract` - 减法运算
222
+ - `math.multiply` - 乘法运算
223
+ - `math.divide` - 除法运算
224
+
225
+ ## 📚 设计文档
226
+
227
+ 详细的设计文档请参考:[MCP 装饰器模式设计文档](./docs/MCP-装饰器模式设计文档.md)
228
+
229
+ ## 🤝 贡献
230
+
231
+ 欢迎提交 Issue 和 Pull Request!
232
+
233
+ ## 📄 许可证
234
+
235
+ MIT License
236
+
237
+ ## 🔗 相关链接
238
+
239
+ - [Hono 文档](https://hono.dev/)
240
+ - [MCP 协议规范](https://modelcontextprotocol.io/)
241
+ - [mcp-handler](https://github.com/modelcontextprotocol/typescript-sdk)
242
+
243
+ ## 📞 联系方式
244
+
245
+ 作者: fromsko
246
+
247
+ ---
248
+
249
+ **提示**: 使用 `npx hono-mcp --help` 查看完整的命令行选项。
package/dist/cli.js ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import { serve } from '@hono/node-server';
3
+ import app from './index.js';
4
+ function parseArgs() {
5
+ const args = process.argv.slice(2);
6
+ const options = {};
7
+ for (let i = 0; i < args.length; i++) {
8
+ const arg = args[i];
9
+ if (arg === '--port' || arg === '-p') {
10
+ options.port = parseInt(args[++i], 10);
11
+ }
12
+ else if (arg === '--host' || arg === '-h') {
13
+ options.host = args[++i];
14
+ }
15
+ else if (arg === '--env') {
16
+ options.env = args[++i];
17
+ }
18
+ else if (arg === '--help' || arg === '-help') {
19
+ console.log(`
20
+ Usage: hono-mcp [options]
21
+
22
+ Options:
23
+ --port, -p <port> Port to listen on (default: 3000)
24
+ --host, -h <host> Host to bind to (default: 0.0.0.0)
25
+ --env <environment> Environment: development or production (default: production)
26
+ --help, -help Show this help message
27
+
28
+ Examples:
29
+ hono-mcp # Start server on default port 3000
30
+ hono-mcp --port 8080 # Start server on port 8080
31
+ hono-mcp --port 8080 --host localhost # Start on localhost:8080
32
+ hono-mcp --env development # Start in development mode
33
+ `);
34
+ process.exit(0);
35
+ }
36
+ }
37
+ return options;
38
+ }
39
+ async function main() {
40
+ const options = parseArgs();
41
+ const port = options.port || parseInt(process.env.PORT || '3000', 10);
42
+ const host = options.host || process.env.HOST || '0.0.0.0';
43
+ const env = options.env || process.env.NODE_ENV || 'production';
44
+ console.log(`🚀 Starting Hono MCP Server...`);
45
+ console.log(`📦 Environment: ${env}`);
46
+ console.log(`🌐 Host: ${host}`);
47
+ console.log(`🔌 Port: ${port}`);
48
+ console.log(`📡 MCP Endpoint: http://${host}:${port}/mcp`);
49
+ console.log(`📖 Help: http://${host}:${port}/help`);
50
+ console.log('');
51
+ try {
52
+ const server = await serve({
53
+ fetch: app.fetch,
54
+ port,
55
+ hostname: host,
56
+ });
57
+ console.log(`✅ Server is running!`);
58
+ console.log(``);
59
+ console.log(`Press Ctrl+C to stop the server`);
60
+ process.on('SIGINT', () => {
61
+ console.log('\n\n🛑 Shutting down server...');
62
+ server.close(() => {
63
+ console.log('✅ Server stopped');
64
+ process.exit(0);
65
+ });
66
+ });
67
+ process.on('SIGTERM', () => {
68
+ console.log('\n\n🛑 Shutting down server...');
69
+ server.close(() => {
70
+ console.log('✅ Server stopped');
71
+ process.exit(0);
72
+ });
73
+ });
74
+ }
75
+ catch (error) {
76
+ console.error('❌ Failed to start server:', error);
77
+ process.exit(1);
78
+ }
79
+ }
80
+ main();
@@ -0,0 +1,105 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { z } from 'zod';
11
+ import { Command, Param } from '../decorators/command.js';
12
+ let AddCommand = class AddCommand {
13
+ a;
14
+ b;
15
+ async execute() {
16
+ const result = this.a + this.b;
17
+ return {
18
+ content: [{ type: 'text', text: `${this.a} + ${this.b} = ${result}` }],
19
+ };
20
+ }
21
+ };
22
+ __decorate([
23
+ Param(z.number().describe('First number')),
24
+ __metadata("design:type", Number)
25
+ ], AddCommand.prototype, "a", void 0);
26
+ __decorate([
27
+ Param(z.number().describe('Second number')),
28
+ __metadata("design:type", Number)
29
+ ], AddCommand.prototype, "b", void 0);
30
+ AddCommand = __decorate([
31
+ Command('math.add', 'Add two numbers')
32
+ ], AddCommand);
33
+ export { AddCommand };
34
+ let SubtractCommand = class SubtractCommand {
35
+ a;
36
+ b;
37
+ async execute() {
38
+ const result = this.a - this.b;
39
+ return {
40
+ content: [{ type: 'text', text: `${this.a} - ${this.b} = ${result}` }],
41
+ };
42
+ }
43
+ };
44
+ __decorate([
45
+ Param(z.number().describe('First number')),
46
+ __metadata("design:type", Number)
47
+ ], SubtractCommand.prototype, "a", void 0);
48
+ __decorate([
49
+ Param(z.number().describe('Second number')),
50
+ __metadata("design:type", Number)
51
+ ], SubtractCommand.prototype, "b", void 0);
52
+ SubtractCommand = __decorate([
53
+ Command('math.subtract', 'Subtract two numbers')
54
+ ], SubtractCommand);
55
+ export { SubtractCommand };
56
+ let MultiplyCommand = class MultiplyCommand {
57
+ a;
58
+ b;
59
+ async execute() {
60
+ const result = this.a * this.b;
61
+ return {
62
+ content: [{ type: 'text', text: `${this.a} × ${this.b} = ${result}` }],
63
+ };
64
+ }
65
+ };
66
+ __decorate([
67
+ Param(z.number().describe('First number')),
68
+ __metadata("design:type", Number)
69
+ ], MultiplyCommand.prototype, "a", void 0);
70
+ __decorate([
71
+ Param(z.number().describe('Second number')),
72
+ __metadata("design:type", Number)
73
+ ], MultiplyCommand.prototype, "b", void 0);
74
+ MultiplyCommand = __decorate([
75
+ Command('math.multiply', 'Multiply two numbers')
76
+ ], MultiplyCommand);
77
+ export { MultiplyCommand };
78
+ let DivideCommand = class DivideCommand {
79
+ a;
80
+ b;
81
+ async execute() {
82
+ if (this.b === 0) {
83
+ return {
84
+ content: [{ type: 'text', text: 'Error: Division by zero' }],
85
+ isError: true,
86
+ };
87
+ }
88
+ const result = this.a / this.b;
89
+ return {
90
+ content: [{ type: 'text', text: `${this.a} ÷ ${this.b} = ${result}` }],
91
+ };
92
+ }
93
+ };
94
+ __decorate([
95
+ Param(z.number().describe('First number')),
96
+ __metadata("design:type", Number)
97
+ ], DivideCommand.prototype, "a", void 0);
98
+ __decorate([
99
+ Param(z.number().describe('Second number')),
100
+ __metadata("design:type", Number)
101
+ ], DivideCommand.prototype, "b", void 0);
102
+ DivideCommand = __decorate([
103
+ Command('math.divide', 'Divide two numbers')
104
+ ], DivideCommand);
105
+ export { DivideCommand };
@@ -0,0 +1,188 @@
1
+ {
2
+ "security.workspace.trust.untrustedFiles": "open",
3
+ "workbench.colorTheme": "One Dark Modern",
4
+ "editor.fontFamily": "'JetBrains Mono', 'maple mono', 'Fira Code', 'Consolas', monospace",
5
+ "editor.tabSize": 4,
6
+ "editor.fontSize": 17,
7
+ "editor.fontLigatures": true,
8
+ "editor.fontWeight": 100,
9
+ "editor.lineNumbers": "on",
10
+ "editor.cursorStyle": "line-thin",
11
+ "editor.wordWrap": "on",
12
+ "editor.scrollBeyondLastLine": false,
13
+ "editor.renderControlCharacters": false,
14
+ "editor.linkedEditing": true,
15
+ "editor.mouseWheelZoom": true,
16
+ "editor.multiCursorModifier": "alt",
17
+ "editor.dragAndDrop": true,
18
+ "editor.showUnused": true,
19
+ "editor.emptySelectionClipboard": true,
20
+ "editor.accessibilityPageSize": 500,
21
+ "editor.minimap.maxColumn": 70,
22
+ "editor.minimap.renderCharacters": true,
23
+ "editor.minimap.showSlider": "always",
24
+ "editor.tokenColorCustomizations": {
25
+ "textMateRules": [
26
+ {
27
+ "name": "Comments",
28
+ "scope": "comment, punctuation.definition.comment",
29
+ "settings": {
30
+ "foreground": "#458b29"
31
+ }
32
+ }
33
+ ]
34
+ },
35
+ "editor.unicodeHighlight.allowedLocales": {
36
+ "ja": true
37
+ },
38
+ "editor.formatOnSave": true,
39
+ "editor.formatOnPaste": true,
40
+ "editor.formatOnType": true,
41
+ "editor.formatOnSaveMode": "file",
42
+ "editor.foldingImportsByDefault": false,
43
+ "editor.codeActionsOnSave": {
44
+ "source.organizeImports": "always",
45
+ "source.fixAll.stylelint": "always",
46
+ "source.fixAll.tsserver": "always",
47
+ "source.unusedImports": "always"
48
+ },
49
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
50
+ "prettier.semi": true,
51
+ "editor.quickSuggestions": {
52
+ "other": "on",
53
+ "comments": "on",
54
+ "strings": "on"
55
+ },
56
+ "editor.quickSuggestionsDelay": 0,
57
+ "editor.suggest.matchOnWordStartOnly": true,
58
+ "editor.suggestFontSize": 14,
59
+ "editor.suggestLineHeight": 22,
60
+ "editor.suggest.preview": true,
61
+ "editor.suggest.showStatusBar": true,
62
+ "editor.suggest.filterGraceful": true,
63
+ "editor.suggest.shareSuggestSelections": true,
64
+ "editor.suggestSelection": "first",
65
+ "editor.suggest.showIcons": true,
66
+ "editor.suggest.showMethods": true,
67
+ "editor.suggest.showFunctions": true,
68
+ "editor.suggest.showConstructors": true,
69
+ "editor.suggest.showFields": true,
70
+ "editor.suggest.showVariables": true,
71
+ "editor.suggest.showUnits": true,
72
+ "editor.suggest.showValues": true,
73
+ "editor.suggest.showSnippets": true,
74
+ "editor.suggest.showKeywords": true,
75
+ "editor.suggest.showWords": true,
76
+ "editor.suggest.showOperators": true,
77
+ "editor.suggest.insertMode": "insert",
78
+ "editor.suggest.snippetsPreventQuickSuggestions": false,
79
+ "editor.suggest.localityBonus": true,
80
+ "editor.suggestOnTriggerCharacters": true,
81
+ "editor.acceptSuggestionOnCommitCharacter": true,
82
+ "editor.acceptSuggestionOnEnter": "smart",
83
+ "editor.parameterHints.enabled": true,
84
+ "editor.inlineSuggest.fontFamily": "'JetBrains Mono', 'Fira Code', 'Consolas', monospace",
85
+ "editor.inlineSuggest.suppressSuggestions": false,
86
+ "editor.autoClosingBrackets": "always",
87
+ "editor.autoClosingQuotes": "always",
88
+ "editor.autoSurround": "languageDefined",
89
+ "editor.largeFileOptimizations": false,
90
+ "tailwindCSS.includeLanguages": {
91
+ "plaintext": "html"
92
+ },
93
+ "files.autoSave": "afterDelay",
94
+ "workbench.editor.doubleClickTabToToggleEditorGroupSizes": "maximize",
95
+ "files.autoSaveDelay": 1000,
96
+ "files.refactoring.autoSave": true,
97
+ "files.associations": {
98
+ "*.vue": "vue",
99
+ "*.cjs": "javascript",
100
+ "*.mjs": "javascript",
101
+ "*.css": "tailwindcss"
102
+ },
103
+ "terminal.integrated.tabs.enabled": true,
104
+ "terminal.integrated.cursorStyle": "line",
105
+ "terminal.integrated.cursorBlinking": true,
106
+ "terminal.integrated.cursorStyleInactive": "line",
107
+ "terminal.integrated.fontFamily": "'JetBrains Mono', Consolas, 'Fira Code', monospace",
108
+ "terminal.integrated.enableMultiLinePasteWarning": "never",
109
+ "git.ignoreMissingGitWarning": true,
110
+ "git.confirmSync": false,
111
+ "git.enableSmartCommit": true,
112
+ "git.autofetch": true,
113
+ "git.openRepositoryInParentFolders": "never",
114
+ "debug.onTaskErrors": "debugAnyway",
115
+ "security.promptForLocalFileProtocolHandling": false,
116
+ "trae.preview.permission": {
117
+ "localhost": {
118
+ "fileSystem": "granted"
119
+ }
120
+ },
121
+ "python.analysis.importFormat": "absolute",
122
+ "python.analysis.autoFormatStrings": false,
123
+ "javascript.updateImportsOnFileMove.enabled": "never",
124
+ "typescript.updateImportsOnFileMove.enabled": "always",
125
+ "extensions.ignoreRecommendations": false,
126
+ "code-runner.clearPreviousOutput": true,
127
+ "code-runner.runInTerminal": true,
128
+ "bracket-pair-colorizer-2.depreciation-notice": false,
129
+ "debug.inlineValues": "on",
130
+ "debug.openDebug": "openOnDebugBreak",
131
+ "workbench.startupEditor": "none",
132
+ "explorer.confirmDelete": false,
133
+ "explorer.confirmDragAndDrop": false,
134
+ "explorer.confirmPasteNative": false,
135
+ "notebook.output.scrolling": true,
136
+ "notebook.output.textLineLimit": 100,
137
+ "path-autocomplete.extensionOnImport": true,
138
+ "path-autocomplete.pathMappings": {
139
+ "./*": "${folder}/*"
140
+ },
141
+ "[markdown]": {
142
+ "editor.defaultFormatter": "yzhang.markdown-all-in-one"
143
+ },
144
+ "[html]": {
145
+ "editor.defaultFormatter": "vscode.html-language-features"
146
+ },
147
+ "[typescript]": {
148
+ "editor.rulers": [120],
149
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
150
+ },
151
+ "[typescriptreact]": {
152
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
153
+ },
154
+ "[jsonc]": {
155
+ "editor.defaultFormatter": "vscode.json-language-features"
156
+ },
157
+ "terminal.integrated.defaultProfile.windows": "Command Prompt",
158
+ "application.extensionMarketUrl": "https://marketplace.visualstudio.com/",
159
+ "[go]": {
160
+ "editor.defaultFormatter": "golang.go"
161
+ },
162
+ "[rust]": {
163
+ "editor.defaultFormatter": "rust-lang.rust-analyzer"
164
+ },
165
+ "workbench.secondarySideBar.defaultVisibility": "hidden",
166
+ "npm.packageManager": "bun",
167
+ "[python]": {
168
+ "editor.defaultFormatter": "charliermarsh.ruff"
169
+ },
170
+ "AI.toolcall.confirmMode": "autoRun",
171
+ "update.showReleaseNotes": false,
172
+ "explorer.compactFolders": false,
173
+ "rest-client.enableTelemetry": false,
174
+ "workbench.iconTheme": "material-icon-theme",
175
+ "workbench.view.showQuietly": {
176
+ "workbench.panel.output": false
177
+ },
178
+ "breadcrumbs.enabled": false,
179
+ "trae.tab.enableSummaryAutoDisplay": true,
180
+ "diffEditor.ignoreTrimWhitespace": false,
181
+ "go.lintTool": "golangci-lint-v2",
182
+ "liveServer.settings.donotVerifyTags": true,
183
+ "liveServer.settings.donotShowInfoMsg": true,
184
+ "cursor-infinity.autoOpenPanel": false,
185
+ "files.participants.timeout": 0,
186
+ "chat.detectParticipant.enabled": false,
187
+ "chat.disableAIFeatures": true
188
+ }
@@ -0,0 +1,37 @@
1
+ import "reflect-metadata";
2
+ const PARAM_METADATA_KEY = "command:params";
3
+ const commandRegistry = new Map();
4
+ export function Command(type, description) {
5
+ return function (constructor) {
6
+ const instance = new constructor();
7
+ const params = new Map();
8
+ const paramMetadata = Reflect.getMetadata(PARAM_METADATA_KEY, constructor) || {};
9
+ Object.entries(paramMetadata).forEach(([key, schema]) => {
10
+ params.set(key, schema);
11
+ });
12
+ commandRegistry.set(type, {
13
+ type,
14
+ description,
15
+ params,
16
+ handler: instance.execute.bind(instance),
17
+ });
18
+ return constructor;
19
+ };
20
+ }
21
+ export function Param(schema) {
22
+ return function (target, propertyKey) {
23
+ const constructor = target.constructor;
24
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, constructor) || {};
25
+ existingParams[propertyKey] = schema;
26
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, constructor);
27
+ };
28
+ }
29
+ export function getCommandRegistry() {
30
+ return commandRegistry;
31
+ }
32
+ export function getCommand(type) {
33
+ return commandRegistry.get(type);
34
+ }
35
+ export function getAllCommands() {
36
+ return Array.from(commandRegistry.values());
37
+ }
package/dist/index.js ADDED
@@ -0,0 +1,40 @@
1
+ import 'reflect-metadata';
2
+ import { Hono } from "hono";
3
+ import { createMcpHandler } from "mcp-handler";
4
+ import vscodeSettings from './data/vscode-settings.json' with { type: 'json' };
5
+ import { executeCommandTool, helpTool } from "./tools/decorator-tools.js";
6
+ import './commands/math.js';
7
+ const app = new Hono();
8
+ const tools = [executeCommandTool, helpTool];
9
+ var ToolName;
10
+ (function (ToolName) {
11
+ ToolName["ExecuteCommand"] = "executeCommand";
12
+ ToolName["Help"] = "help";
13
+ })(ToolName || (ToolName = {}));
14
+ const handler = createMcpHandler((server) => {
15
+ tools.forEach((tool) => {
16
+ tool(server);
17
+ });
18
+ }, {}, {
19
+ basePath: "/",
20
+ maxDuration: 60,
21
+ verboseLogs: true,
22
+ });
23
+ app.all("/mcp/*", async (c) => {
24
+ return await handler(c.req.raw);
25
+ });
26
+ app.get("/vscode", async (c) => {
27
+ return c.json(vscodeSettings);
28
+ });
29
+ app.get("/", async (c) => {
30
+ return c.json({
31
+ message: "Hono MCP Server - Math Operations",
32
+ endpoints: {
33
+ mcp: "/mcp",
34
+ testEdgeConfig: "/test-edge-config",
35
+ description: "MCP server with math operation tools (add, subtract, multiply, divide)",
36
+ },
37
+ tools: Object.values(ToolName),
38
+ });
39
+ });
40
+ export default app;
@@ -0,0 +1,93 @@
1
+ import { z } from "zod";
2
+ import { getCommandRegistry } from "../decorators/command.js";
3
+ export const executeCommandTool = (server) => {
4
+ server.tool("executeCommand", "Execute any command by specifying its type and parameters", {
5
+ type: {
6
+ type: "string",
7
+ description: "Command type (e.g., math.add, math.subtract, math.multiply, math.divide)",
8
+ },
9
+ params: {
10
+ type: "object",
11
+ description: "Command parameters",
12
+ },
13
+ }, async ({ type, params }) => {
14
+ const command = getCommandRegistry().get(type);
15
+ if (!command) {
16
+ return {
17
+ content: [
18
+ {
19
+ type: "text",
20
+ text: `Unknown command type: ${type}\n\nAvailable commands:\n${Array.from(getCommandRegistry().keys())
21
+ .map((t) => `- ${t}`)
22
+ .join("\n")}`,
23
+ },
24
+ ],
25
+ isError: true,
26
+ };
27
+ }
28
+ const schema = z.object(Object.fromEntries(Array.from(command.params.entries()).map(([key, val]) => [key, val])));
29
+ const validationResult = schema.safeParse(params);
30
+ if (!validationResult.success) {
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: `Invalid parameters: ${validationResult.error.message}`,
36
+ },
37
+ ],
38
+ isError: true,
39
+ };
40
+ }
41
+ return await command.handler(validationResult.data);
42
+ });
43
+ };
44
+ export const helpTool = (server) => {
45
+ server.tool("help", "Get help information about available commands", {
46
+ type: {
47
+ type: "string",
48
+ description: "Command type to get help for (optional)",
49
+ },
50
+ }, async ({ type }) => {
51
+ if (type) {
52
+ const command = getCommandRegistry().get(type);
53
+ if (!command) {
54
+ return {
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: `Unknown command type: ${type}\n\nAvailable commands:\n${Array.from(getCommandRegistry().keys())
59
+ .map((t) => `- ${t}`)
60
+ .join("\n")}`,
61
+ },
62
+ ],
63
+ isError: true,
64
+ };
65
+ }
66
+ const paramsInfo = Array.from(command.params.entries())
67
+ .map(([key, schema]) => {
68
+ const description = schema.description || "No description";
69
+ return `- ${key}: ${description}`;
70
+ })
71
+ .join("\n");
72
+ return {
73
+ content: [
74
+ {
75
+ type: "text",
76
+ text: `Command: ${command.type}\nDescription: ${command.description}\n\nParameters:\n${paramsInfo}`,
77
+ },
78
+ ],
79
+ };
80
+ }
81
+ const commandList = Array.from(getCommandRegistry().values())
82
+ .map((cmd) => `- ${cmd.type}: ${cmd.description}`)
83
+ .join("\n");
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: `Available commands:\n${commandList}\n\nUse help with a specific command type for details.`,
89
+ },
90
+ ],
91
+ };
92
+ });
93
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "hono-mcp",
3
+ "version": "1.3.0",
4
+ "description": "MCP server built with Hono - supports both Vercel deployment and npx CLI usage",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "hono-mcp": "dist/cli.js"
8
+ },
9
+ "type": "module",
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/cli.js",
17
+ "dev": "tsx src/cli.ts",
18
+ "dev:vercel": "tsx src/index.ts"
19
+ },
20
+ "keywords": [
21
+ "mcp",
22
+ "hono",
23
+ "model-context-protocol",
24
+ "mcp-server",
25
+ "cli",
26
+ "npx",
27
+ "vercel",
28
+ "server",
29
+ "api",
30
+ "typescript",
31
+ "decorator"
32
+ ],
33
+ "author": "fromsko",
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "@hono/node-server": "^1.19.7",
37
+ "@modelcontextprotocol/sdk": "^1.17.3",
38
+ "@vercel/edge-config": "^1.4.3",
39
+ "hono": "^4.9.2",
40
+ "mcp-handler": "^1.0.1",
41
+ "reflect-metadata": "^0.2.2",
42
+ "zod": "^3"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20.11.17",
46
+ "tsx": "^4.7.0",
47
+ "typescript": "^5.9.3"
48
+ }
49
+ }