modality-mcp-kit 1.3.0 → 1.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.
@@ -24,11 +24,13 @@
24
24
  * https://modelcontextprotocol.io/specification/2025-11-25/schema
25
25
  */
26
26
  import { ModalityFastMCP } from "./util_mcp_tools_converter.js";
27
+ import { createMcpConnectionDemoHandler } from "./util_mcp_connection_demo.js";
27
28
  import { JSONRPCManager, getLoggerInstance, } from "modality-kit";
28
29
  import { sseNotification, sseError, SSE_HEADERS, createSSEStream, } from "./sse-wrapper.js";
29
30
  import { McpSessionManager } from "./McpSessionManager.js";
30
31
  import { handleToolCall } from "./handlers/tools-call-handler.js";
31
32
  const defaultMcpPath = "/mcp";
33
+ const defaultMcpDemoPath = "/";
32
34
  const mcpSchemaVersion = "2025-11-25";
33
35
  // Initialize FastMCP instance for internal use (NO SERVER)
34
36
  export class FastHonoMcp extends ModalityFastMCP {
@@ -41,6 +43,20 @@ export class FastHonoMcp extends ModalityFastMCP {
41
43
  super();
42
44
  this.config = config;
43
45
  }
46
+ initHono(app) {
47
+ const { name, version, helloWorld, mcpPath = defaultMcpPath, mcpDemoPath = defaultMcpDemoPath, } = this.config;
48
+ this.mcpPath = mcpPath;
49
+ const middlewareHandler = this.handler();
50
+ app.use(mcpPath, middlewareHandler);
51
+ app.use(`${mcpPath}/*`, middlewareHandler);
52
+ app.get(mcpDemoPath, createMcpConnectionDemoHandler({
53
+ serverName: name,
54
+ serverVersion: version,
55
+ mcpPath,
56
+ helloWorld,
57
+ }));
58
+ return this;
59
+ }
44
60
  /**
45
61
  * Disconnect and cleanup a session
46
62
  */
@@ -125,13 +141,6 @@ export class FastHonoMcp extends ModalityFastMCP {
125
141
  }
126
142
  };
127
143
  }
128
- initHono(app, path = defaultMcpPath) {
129
- this.mcpPath = path;
130
- const middlewareHandler = this.handler();
131
- app.use(path, middlewareHandler);
132
- app.use(`${path}/*`, middlewareHandler);
133
- return this;
134
- }
135
144
  }
136
145
  class HonoJSONRPCManager extends JSONRPCManager {
137
146
  async sendMessage(message) {
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { toJsonSchema } from "xsschema";
2
2
  export { setupAITools, ModalityFastMCP } from "./util_mcp_tools_converter";
3
3
  export { FastHonoMcp } from "./FastHonoMcp";
4
+ export { mcpProxyHandler, mcpProxyListHandler, mcpProxyCacheHandler, } from "./util_mcp_proxy";
@@ -30,14 +30,18 @@ import { McpSessionManager } from "./McpSessionManager.js";
30
30
  export interface FastHonoMcpConfig extends Record<string, unknown> {
31
31
  name: string;
32
32
  version: string;
33
+ mcpPath?: string;
34
+ mcpDemoPath?: string;
35
+ helloWorld?: string;
33
36
  }
34
37
  export declare class FastHonoMcp extends ModalityFastMCP {
35
38
  logger: ReturnType<typeof getLoggerInstance>;
36
39
  config: FastHonoMcpConfig;
37
40
  sessions: McpSessionManager;
38
41
  private currentSessionId;
39
- private mcpPath;
42
+ mcpPath: string;
40
43
  constructor(config: FastHonoMcpConfig);
44
+ initHono(app: Hono): this;
41
45
  /**
42
46
  * Disconnect and cleanup a session
43
47
  */
@@ -47,5 +51,4 @@ export declare class FastHonoMcp extends ModalityFastMCP {
47
51
  */
48
52
  private ensureSession;
49
53
  handler(): MiddlewareHandler;
50
- initHono(app: Hono, path?: string): this;
51
54
  }
@@ -3,3 +3,4 @@ export { setupAITools, ModalityFastMCP } from "./util_mcp_tools_converter";
3
3
  export type { AITools, AITool, FastMCPTool, } from "./schemas/schemas_tool_config";
4
4
  export type { FastMCPCompatible, BasePrompt, } from "./util_mcp_tools_converter";
5
5
  export { FastHonoMcp } from "./FastHonoMcp";
6
+ export { mcpProxyHandler, mcpProxyListHandler, mcpProxyCacheHandler, type McpProxyConfig, } from "./util_mcp_proxy";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MCP Connection Demo Handler
3
+ *
4
+ * Provides an interactive demonstration page for MCP connection patterns
5
+ * with examples showing how to connect various AI tools and development utilities.
6
+ *
7
+ * Usage: /mcp-demo - returns MCP connection demo documentation and tool showcase
8
+ */
9
+ interface MCPConnectionConfig {
10
+ serverName: string;
11
+ serverVersion?: string;
12
+ serverUrl?: string;
13
+ mcpPath?: string;
14
+ defaultFormat?: "json" | "markdown" | "html";
15
+ helloWorld?: string;
16
+ }
17
+ /**
18
+ * Create MCP connection demo handler with server configuration
19
+ * @param config - Server name and version for consistency
20
+ * @returns Hono handler function
21
+ */
22
+ export declare const createMcpConnectionDemoHandler: (config: MCPConnectionConfig) => (c: any) => Promise<any>;
23
+ /**
24
+ * Hono handler for MCP connection demo
25
+ * Returns documentation and tool showcase data
26
+ * @param c - Hono context
27
+ * @param config - Optional server configuration
28
+ * @returns Demo page with connection information
29
+ */
30
+ export declare const mcpConnectionDemoHandler: (c: any, config?: MCPConnectionConfig) => Promise<any>;
31
+ export {};
@@ -0,0 +1,496 @@
1
+ /**
2
+ * MCP Connection Demo Handler
3
+ *
4
+ * Provides an interactive demonstration page for MCP connection patterns
5
+ * with examples showing how to connect various AI tools and development utilities.
6
+ *
7
+ * Usage: /mcp-demo - returns MCP connection demo documentation and tool showcase
8
+ */
9
+ const defaultOutputFormat = "html";
10
+ const connectAIShowcase = (serverName, serverUrl, mcpPath = "/mcp") => ({
11
+ claudeCode: {
12
+ name: "Claude Code",
13
+ description: "Official Claude IDE tool for seamless development workflow",
14
+ connectionCode: `claude mcp add -s user --transport http ${serverName} ${serverUrl}${mcpPath}`,
15
+ type: "editor",
16
+ },
17
+ githubCli: {
18
+ name: "GitHub CLI",
19
+ description: "Command-line tool with MCP support for GitHub operations",
20
+ connectionCode: `vim ~/.copilot/mcp-config.json
21
+
22
+ {
23
+ "mcpServers": {
24
+ "${serverName}": {
25
+ "type": "http",
26
+ "url": "${serverUrl}${mcpPath}",
27
+ "headers": {},
28
+ "tools": ["*"]
29
+ }
30
+ }
31
+ }`,
32
+ type: "terminal",
33
+ },
34
+ vscode: {
35
+ name: "VS Code",
36
+ description: "Popular code editor with MCP extension support",
37
+ connectionCode: `code --add-mcp '{"name":"${serverName}", "url": "${serverUrl}${mcpPath}", "type": "http"}'`,
38
+ type: "editor",
39
+ },
40
+ mytyAi: {
41
+ name: "Myty AI",
42
+ description: "AI assistant platform with comprehensive MCP capabilities",
43
+ connectionCode: `{
44
+ "mcpServers": {
45
+ "${serverName}": {
46
+ "command": "npx",
47
+ "args": [
48
+ "mcp-remote",
49
+ "${serverUrl}${mcpPath}"
50
+ ]
51
+ }
52
+ }
53
+ }`,
54
+ type: "platform",
55
+ },
56
+ });
57
+ // ============================================
58
+ // DEMO CONTENT - MARKDOWN-BASED
59
+ // ============================================
60
+ const generateDemoDocumentation = (serverName, serverUrl, mcpPath = "/mcp", helloWorld) => {
61
+ const tools = connectAIShowcase(serverName, serverUrl, mcpPath);
62
+ return `# MCP Connection Guide
63
+
64
+ ${helloWorld ? `## Hello Prompt\n\n${helloWorld}\n` : ""}## How to Connect
65
+
66
+ 1. Copy the connection code for your tool
67
+ 2. Add it to your MCP configuration
68
+ 3. Establish the session
69
+
70
+ ## Connection Codes
71
+
72
+ ${Object.entries(tools)
73
+ .map(([, tool]) => `### ${tool.name}
74
+ \`\`\`
75
+ ${tool.connectionCode}
76
+ \`\`\``)
77
+ .join("\n\n")}
78
+ `;
79
+ };
80
+ // ============================================
81
+ // HONO HANDLER
82
+ // ============================================
83
+ /**
84
+ * Create MCP connection demo handler with server configuration
85
+ * @param config - Server name and version for consistency
86
+ * @returns Hono handler function
87
+ */
88
+ export const createMcpConnectionDemoHandler = (config) => {
89
+ return async (c) => {
90
+ return mcpConnectionDemoHandler(c, config);
91
+ };
92
+ };
93
+ /**
94
+ * Hono handler for MCP connection demo
95
+ * Returns documentation and tool showcase data
96
+ * @param c - Hono context
97
+ * @param config - Optional server configuration
98
+ * @returns Demo page with connection information
99
+ */
100
+ export const mcpConnectionDemoHandler = async (c, config) => {
101
+ const format = c.req.query("format") || config?.defaultFormat || defaultOutputFormat;
102
+ const serverName = config?.serverName || "mcp-server";
103
+ const mcpPath = config?.mcpPath || "/mcp";
104
+ // Get server URL: from config first, then extract from request
105
+ let serverUrl = "";
106
+ if (!config?.serverUrl && c.req.url) {
107
+ try {
108
+ const url = new URL(c.req.url);
109
+ serverUrl = `${url.protocol}//${url.host}`;
110
+ }
111
+ catch {
112
+ // Fallback to default if URL parsing fails
113
+ }
114
+ }
115
+ else if (config?.serverUrl) {
116
+ serverUrl = config?.serverUrl;
117
+ }
118
+ const tools = connectAIShowcase(serverName, serverUrl, mcpPath);
119
+ const documentation = generateDemoDocumentation(serverName, serverUrl, mcpPath, config?.helloWorld);
120
+ // Handle different format requests
121
+ if (format === "markdown" || format === "md") {
122
+ return new Response(documentation, {
123
+ status: 200,
124
+ headers: {
125
+ "Content-Type": "text/markdown; charset=utf-8",
126
+ "Cache-Control": "public, max-age=3600",
127
+ "Access-Control-Allow-Origin": "*",
128
+ },
129
+ });
130
+ }
131
+ if (format === "html") {
132
+ const htmlContent = generateHtmlPage(serverUrl, config, tools, mcpPath);
133
+ return new Response(htmlContent, {
134
+ status: 200,
135
+ headers: {
136
+ "Content-Type": "text/html; charset=utf-8",
137
+ "Cache-Control": "public, max-age=3600",
138
+ "Access-Control-Allow-Origin": "*",
139
+ },
140
+ });
141
+ }
142
+ // Default: JSON format with complete data
143
+ const demoData = {
144
+ documentation,
145
+ tools: Object.entries(tools).map(([key, tool]) => ({
146
+ id: key,
147
+ ...tool,
148
+ })),
149
+ server: {
150
+ name: serverName,
151
+ version: config?.serverVersion || "unknown",
152
+ url: serverUrl,
153
+ mcpPath: mcpPath,
154
+ },
155
+ metadata: {
156
+ toolCount: Object.keys(tools).length,
157
+ lastUpdated: new Date().toISOString(),
158
+ format: "json",
159
+ availableFormats: ["json", "markdown", "html"],
160
+ },
161
+ };
162
+ return c.json(demoData, {
163
+ headers: {
164
+ "Cache-Control": "public, max-age=3600",
165
+ "Access-Control-Allow-Origin": "*",
166
+ },
167
+ });
168
+ };
169
+ // ============================================
170
+ // HTML GENERATION
171
+ // ============================================
172
+ function escapeHtml(text) {
173
+ const map = {
174
+ "&": "&amp;",
175
+ "<": "&lt;",
176
+ ">": "&gt;",
177
+ '"': "&quot;",
178
+ "'": "&#039;",
179
+ };
180
+ return text.replace(/[&<>"']/g, (char) => map[char] || char);
181
+ }
182
+ function escapeForJs(text) {
183
+ return text.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
184
+ }
185
+ function generateHtmlPage(serverUrl, config, tools, mcpPath = "/mcp") {
186
+ const toolsToDisplay = tools || connectAIShowcase(config?.serverName || "mcp-server", serverUrl, mcpPath);
187
+ const toolsHtml = Object.entries(toolsToDisplay)
188
+ .map(([, tool]) => `
189
+ <div class="tool-card">
190
+ <h3>${tool.name}</h3>
191
+ <p class="tool-type">${tool.type}</p>
192
+ <p class="tool-description">${tool.description}</p>
193
+ <div class="connection-section">
194
+ <label>Connection Code</label>
195
+ <pre class="code-block"><code>${escapeHtml(tool.connectionCode)}</code></pre>
196
+ <button class="copy-btn" onclick="copyToClipboard(\`${escapeForJs(tool.connectionCode)}\`)">
197
+ Copy
198
+ </button>
199
+ </div>
200
+ </div>
201
+ `)
202
+ .join("\n");
203
+ return `
204
+ <!DOCTYPE html>
205
+ <html lang="en">
206
+ <head>
207
+ <meta charset="UTF-8">
208
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
209
+ <title>${config?.serverName || "MCP Connection Demo"}</title>
210
+ <style>
211
+ * {
212
+ margin: 0;
213
+ padding: 0;
214
+ box-sizing: border-box;
215
+ }
216
+
217
+ body {
218
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
219
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
220
+ min-height: 100vh;
221
+ padding: 2rem;
222
+ }
223
+
224
+ .container {
225
+ max-width: 1200px;
226
+ margin: 0 auto;
227
+ background: white;
228
+ border-radius: 12px;
229
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
230
+ overflow: hidden;
231
+ }
232
+
233
+ header {
234
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
235
+ color: white;
236
+ padding: 3rem 2rem;
237
+ text-align: center;
238
+ }
239
+
240
+ header h1 {
241
+ font-size: 2.5rem;
242
+ margin-bottom: 0.5rem;
243
+ }
244
+
245
+ header p {
246
+ font-size: 1.1rem;
247
+ opacity: 0.9;
248
+ }
249
+
250
+ .content {
251
+ padding: 2rem;
252
+ }
253
+
254
+ .intro-section {
255
+ margin-bottom: 3rem;
256
+ padding: 2rem;
257
+ background: #f8f9fa;
258
+ border-radius: 8px;
259
+ border-left: 4px solid #667eea;
260
+ }
261
+
262
+ .intro-section h2 {
263
+ color: #333;
264
+ margin-bottom: 1rem;
265
+ font-size: 1.5rem;
266
+ }
267
+
268
+ .intro-section p {
269
+ color: #666;
270
+ line-height: 1.6;
271
+ margin-bottom: 1rem;
272
+ }
273
+
274
+ .tools-section {
275
+ margin-top: 3rem;
276
+ }
277
+
278
+ .tools-section h2 {
279
+ color: #333;
280
+ margin-bottom: 2rem;
281
+ font-size: 1.5rem;
282
+ border-bottom: 2px solid #667eea;
283
+ padding-bottom: 1rem;
284
+ }
285
+
286
+ .tools-grid {
287
+ display: grid;
288
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
289
+ gap: 2rem;
290
+ }
291
+
292
+ .tool-card {
293
+ border: 1px solid #e0e0e0;
294
+ border-radius: 8px;
295
+ padding: 1.5rem;
296
+ transition: all 0.3s ease;
297
+ background: white;
298
+ }
299
+
300
+ .tool-card:hover {
301
+ border-color: #667eea;
302
+ box-shadow: 0 8px 24px rgba(102, 126, 234, 0.15);
303
+ transform: translateY(-2px);
304
+ }
305
+
306
+ .tool-card h3 {
307
+ color: #333;
308
+ margin-bottom: 0.5rem;
309
+ font-size: 1.3rem;
310
+ }
311
+
312
+ .tool-type {
313
+ display: inline-block;
314
+ background: #667eea;
315
+ color: white;
316
+ padding: 0.25rem 0.75rem;
317
+ border-radius: 20px;
318
+ font-size: 0.75rem;
319
+ font-weight: 600;
320
+ text-transform: uppercase;
321
+ margin-bottom: 1rem;
322
+ }
323
+
324
+ .tool-description {
325
+ color: #666;
326
+ margin-bottom: 1.5rem;
327
+ line-height: 1.6;
328
+ font-size: 0.95rem;
329
+ }
330
+
331
+ .connection-section {
332
+ margin-top: 1rem;
333
+ }
334
+
335
+ .connection-section label {
336
+ display: block;
337
+ color: #333;
338
+ font-weight: 600;
339
+ margin-bottom: 0.5rem;
340
+ font-size: 0.9rem;
341
+ }
342
+
343
+ .url-container {
344
+ display: flex;
345
+ gap: 0.5rem;
346
+ align-items: center;
347
+ }
348
+
349
+ code {
350
+ background: transparent;
351
+ padding: 0;
352
+ border: none;
353
+ color: #333;
354
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
355
+ font-size: 0.85rem;
356
+ word-break: break-all;
357
+ }
358
+
359
+ .code-block {
360
+ background: #f5f5f5;
361
+ padding: 1rem;
362
+ border-radius: 4px;
363
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
364
+ font-size: 0.85rem;
365
+ color: #333;
366
+ border: 1px solid #e0e0e0;
367
+ overflow-x: auto;
368
+ margin: 0.5rem 0 1rem 0;
369
+ white-space: pre-wrap;
370
+ word-wrap: break-word;
371
+ }
372
+
373
+ .copy-btn {
374
+ background: #667eea;
375
+ color: white;
376
+ border: none;
377
+ padding: 0.5rem 1rem;
378
+ border-radius: 4px;
379
+ cursor: pointer;
380
+ font-weight: 600;
381
+ font-size: 0.85rem;
382
+ transition: background 0.2s ease;
383
+ white-space: nowrap;
384
+ }
385
+
386
+ .copy-btn:hover {
387
+ background: #764ba2;
388
+ }
389
+
390
+ .copy-btn:active {
391
+ transform: scale(0.98);
392
+ }
393
+
394
+ footer {
395
+ background: #f8f9fa;
396
+ padding: 2rem;
397
+ text-align: center;
398
+ color: #666;
399
+ border-top: 1px solid #e0e0e0;
400
+ font-size: 0.9rem;
401
+ }
402
+
403
+ @media (max-width: 768px) {
404
+ header h1 {
405
+ font-size: 1.8rem;
406
+ }
407
+
408
+ .tools-grid {
409
+ grid-template-columns: 1fr;
410
+ }
411
+
412
+ .content {
413
+ padding: 1rem;
414
+ }
415
+
416
+ .url-container {
417
+ flex-direction: column;
418
+ }
419
+
420
+ .copy-btn {
421
+ width: 100%;
422
+ }
423
+ }
424
+ </style>
425
+ </head>
426
+ <body>
427
+ <div class="container">
428
+ <header>
429
+ <h1>🌐 ${config?.serverName || "MCP Connection Demo"}</h1>
430
+ <p>Interactive guide for connecting AI tools and development utilities</p>
431
+ ${config
432
+ ? `<p style="font-size: 0.95rem; margin-top: 1rem; opacity: 0.8;">Server: <strong>${config.serverName}</strong> v${config.serverVersion || "unknown"}</p>`
433
+ : ""}
434
+ </header>
435
+
436
+ <div class="content">
437
+ ${config?.helloWorld
438
+ ? `<div class="intro-section">
439
+ <h2>Hello Prompt</h2>
440
+ <p>${config.helloWorld}</p>
441
+ </div>`
442
+ : `<div class="intro-section">
443
+ <h2>Welcome to MCP</h2>
444
+ <p>
445
+ The Model Context Protocol (MCP) provides a standardized way for AI tools
446
+ and development utilities to exchange information and capabilities.
447
+ </p>
448
+ <p>
449
+ Below you'll find connection URLs for popular tools. Copy the URL for your
450
+ tool and add it to your MCP configuration.
451
+ </p>
452
+ </div>`}
453
+
454
+ <div class="tools-section">
455
+ <h2>Connect your AI assistant</h2>
456
+ <div class="tools-grid">
457
+ ${toolsHtml}
458
+ </div>
459
+ </div>
460
+ </div>
461
+
462
+ <footer>
463
+ <p>${config?.serverName || "MCP Connection Demo"} &copy; 2026 | Last updated: ${new Date().toLocaleDateString()}</p>
464
+ ${config
465
+ ? `<p>Server: <strong>${config.serverName}</strong> | URL: <code>${serverUrl}</code></p>`
466
+ : ""}
467
+ </footer>
468
+ </div>
469
+
470
+ <script>
471
+ function copyToClipboard(text) {
472
+ navigator.clipboard.writeText(text).then(() => {
473
+ const btn = event.target;
474
+ const originalText = btn.textContent;
475
+ btn.textContent = '✓ Copied!';
476
+ setTimeout(() => {
477
+ btn.textContent = originalText;
478
+ }, 2000);
479
+ }).catch(err => {
480
+ console.error('Failed to copy:', err);
481
+ alert('Failed to copy to clipboard');
482
+ });
483
+ }
484
+
485
+ // Log page load performance
486
+ window.addEventListener('load', () => {
487
+ const perfData = performance.getEntriesByType('navigation')[0];
488
+ if (perfData) {
489
+ console.log('Page Load Time:', perfData.loadEventEnd - perfData.fetchStart, 'ms');
490
+ }
491
+ });
492
+ </script>
493
+ </body>
494
+ </html>
495
+ `.trim();
496
+ }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.0",
2
+ "version": "1.3.2",
3
3
  "name": "modality-mcp-kit",
4
4
  "repository": {
5
5
  "type": "git",
@@ -23,7 +23,7 @@
23
23
  "license": "ISC",
24
24
  "peerDependencies": {
25
25
  "zod": "^4.3.5",
26
- "hono": "*"
26
+ "hono": "^4.11.3"
27
27
  },
28
28
  "dependencies": {
29
29
  "@valibot/to-json-schema": "^1.5.0",