@wener/mcps 1.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.
Files changed (141) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.mjs +15 -0
  3. package/dist/mcps-cli.mjs +174727 -0
  4. package/lib/chat/agent.js +187 -0
  5. package/lib/chat/agent.js.map +1 -0
  6. package/lib/chat/audit.js +238 -0
  7. package/lib/chat/audit.js.map +1 -0
  8. package/lib/chat/converters.js +467 -0
  9. package/lib/chat/converters.js.map +1 -0
  10. package/lib/chat/handler.js +1068 -0
  11. package/lib/chat/handler.js.map +1 -0
  12. package/lib/chat/index.js +12 -0
  13. package/lib/chat/index.js.map +1 -0
  14. package/lib/chat/types.js +35 -0
  15. package/lib/chat/types.js.map +1 -0
  16. package/lib/contracts/AuditContract.js +85 -0
  17. package/lib/contracts/AuditContract.js.map +1 -0
  18. package/lib/contracts/McpsContract.js +113 -0
  19. package/lib/contracts/McpsContract.js.map +1 -0
  20. package/lib/contracts/index.js +3 -0
  21. package/lib/contracts/index.js.map +1 -0
  22. package/lib/dev.server.js +7 -0
  23. package/lib/dev.server.js.map +1 -0
  24. package/lib/entities/ChatRequestEntity.js +318 -0
  25. package/lib/entities/ChatRequestEntity.js.map +1 -0
  26. package/lib/entities/McpRequestEntity.js +271 -0
  27. package/lib/entities/McpRequestEntity.js.map +1 -0
  28. package/lib/entities/RequestLogEntity.js +177 -0
  29. package/lib/entities/RequestLogEntity.js.map +1 -0
  30. package/lib/entities/ResponseEntity.js +150 -0
  31. package/lib/entities/ResponseEntity.js.map +1 -0
  32. package/lib/entities/index.js +11 -0
  33. package/lib/entities/index.js.map +1 -0
  34. package/lib/entities/types.js +11 -0
  35. package/lib/entities/types.js.map +1 -0
  36. package/lib/index.js +3 -0
  37. package/lib/index.js.map +1 -0
  38. package/lib/mcps-cli.js +44 -0
  39. package/lib/mcps-cli.js.map +1 -0
  40. package/lib/providers/McpServerHandlerDef.js +40 -0
  41. package/lib/providers/McpServerHandlerDef.js.map +1 -0
  42. package/lib/providers/findMcpServerDef.js +26 -0
  43. package/lib/providers/findMcpServerDef.js.map +1 -0
  44. package/lib/providers/prometheus/def.js +24 -0
  45. package/lib/providers/prometheus/def.js.map +1 -0
  46. package/lib/providers/prometheus/index.js +2 -0
  47. package/lib/providers/prometheus/index.js.map +1 -0
  48. package/lib/providers/relay/def.js +32 -0
  49. package/lib/providers/relay/def.js.map +1 -0
  50. package/lib/providers/relay/index.js +2 -0
  51. package/lib/providers/relay/index.js.map +1 -0
  52. package/lib/providers/sql/def.js +31 -0
  53. package/lib/providers/sql/def.js.map +1 -0
  54. package/lib/providers/sql/index.js +2 -0
  55. package/lib/providers/sql/index.js.map +1 -0
  56. package/lib/providers/tencent-cls/def.js +44 -0
  57. package/lib/providers/tencent-cls/def.js.map +1 -0
  58. package/lib/providers/tencent-cls/index.js +2 -0
  59. package/lib/providers/tencent-cls/index.js.map +1 -0
  60. package/lib/scripts/bundle.js +90 -0
  61. package/lib/scripts/bundle.js.map +1 -0
  62. package/lib/server/api-routes.js +96 -0
  63. package/lib/server/api-routes.js.map +1 -0
  64. package/lib/server/audit.js +274 -0
  65. package/lib/server/audit.js.map +1 -0
  66. package/lib/server/chat-routes.js +82 -0
  67. package/lib/server/chat-routes.js.map +1 -0
  68. package/lib/server/config.js +223 -0
  69. package/lib/server/config.js.map +1 -0
  70. package/lib/server/db.js +97 -0
  71. package/lib/server/db.js.map +1 -0
  72. package/lib/server/index.js +2 -0
  73. package/lib/server/index.js.map +1 -0
  74. package/lib/server/mcp-handler.js +167 -0
  75. package/lib/server/mcp-handler.js.map +1 -0
  76. package/lib/server/mcp-routes.js +112 -0
  77. package/lib/server/mcp-routes.js.map +1 -0
  78. package/lib/server/mcps-router.js +119 -0
  79. package/lib/server/mcps-router.js.map +1 -0
  80. package/lib/server/schema.js +129 -0
  81. package/lib/server/schema.js.map +1 -0
  82. package/lib/server/server.js +166 -0
  83. package/lib/server/server.js.map +1 -0
  84. package/lib/web/ChatPage.js +827 -0
  85. package/lib/web/ChatPage.js.map +1 -0
  86. package/lib/web/McpInspectorPage.js +214 -0
  87. package/lib/web/McpInspectorPage.js.map +1 -0
  88. package/lib/web/ServersPage.js +93 -0
  89. package/lib/web/ServersPage.js.map +1 -0
  90. package/lib/web/main.js +541 -0
  91. package/lib/web/main.js.map +1 -0
  92. package/package.json +83 -0
  93. package/src/chat/agent.ts +240 -0
  94. package/src/chat/audit.ts +377 -0
  95. package/src/chat/converters.test.ts +325 -0
  96. package/src/chat/converters.ts +459 -0
  97. package/src/chat/handler.test.ts +137 -0
  98. package/src/chat/handler.ts +1233 -0
  99. package/src/chat/index.ts +16 -0
  100. package/src/chat/types.ts +72 -0
  101. package/src/contracts/AuditContract.ts +93 -0
  102. package/src/contracts/McpsContract.ts +141 -0
  103. package/src/contracts/index.ts +18 -0
  104. package/src/dev.server.ts +7 -0
  105. package/src/entities/ChatRequestEntity.ts +157 -0
  106. package/src/entities/McpRequestEntity.ts +149 -0
  107. package/src/entities/RequestLogEntity.ts +78 -0
  108. package/src/entities/ResponseEntity.ts +75 -0
  109. package/src/entities/index.ts +12 -0
  110. package/src/entities/types.ts +188 -0
  111. package/src/index.ts +1 -0
  112. package/src/mcps-cli.ts +59 -0
  113. package/src/providers/McpServerHandlerDef.ts +105 -0
  114. package/src/providers/findMcpServerDef.ts +31 -0
  115. package/src/providers/prometheus/def.ts +21 -0
  116. package/src/providers/prometheus/index.ts +1 -0
  117. package/src/providers/relay/def.ts +31 -0
  118. package/src/providers/relay/index.ts +1 -0
  119. package/src/providers/relay/relay.test.ts +47 -0
  120. package/src/providers/sql/def.ts +33 -0
  121. package/src/providers/sql/index.ts +1 -0
  122. package/src/providers/tencent-cls/def.ts +38 -0
  123. package/src/providers/tencent-cls/index.ts +1 -0
  124. package/src/scripts/bundle.ts +82 -0
  125. package/src/server/api-routes.ts +98 -0
  126. package/src/server/audit.ts +310 -0
  127. package/src/server/chat-routes.ts +95 -0
  128. package/src/server/config.test.ts +162 -0
  129. package/src/server/config.ts +198 -0
  130. package/src/server/db.ts +115 -0
  131. package/src/server/index.ts +1 -0
  132. package/src/server/mcp-handler.ts +209 -0
  133. package/src/server/mcp-routes.ts +133 -0
  134. package/src/server/mcps-router.ts +133 -0
  135. package/src/server/schema.ts +175 -0
  136. package/src/server/server.ts +163 -0
  137. package/src/web/ChatPage.tsx +1005 -0
  138. package/src/web/McpInspectorPage.tsx +254 -0
  139. package/src/web/ServersPage.tsx +139 -0
  140. package/src/web/main.tsx +600 -0
  141. package/src/web/styles.css +15 -0
@@ -0,0 +1,254 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import type { ServerInfo } from '../contracts';
3
+
4
+ interface McpTool {
5
+ name: string;
6
+ description?: string;
7
+ inputSchema?: {
8
+ type?: string;
9
+ properties?: Record<string, { type?: string; description?: string }>;
10
+ required?: string[];
11
+ };
12
+ }
13
+
14
+ export function McpInspectorPage() {
15
+ const [servers, setServers] = useState<ServerInfo[]>([]);
16
+ const [selectedServer, setSelectedServer] = useState<string>('');
17
+ const [tools, setTools] = useState<McpTool[]>([]);
18
+ const [selectedTool, setSelectedTool] = useState<McpTool | null>(null);
19
+ const [toolInput, setToolInput] = useState('{}');
20
+ const [toolResult, setToolResult] = useState<string | null>(null);
21
+ const [loading, setLoading] = useState(true);
22
+ const [connecting, setConnecting] = useState(false);
23
+ const [executing, setExecuting] = useState(false);
24
+ const [error, setError] = useState<string | null>(null);
25
+
26
+ // Fetch servers on mount
27
+ useEffect(() => {
28
+ fetch('/api/mcps/servers')
29
+ .then((r) => r.json())
30
+ .then((data) => setServers(data.servers || []))
31
+ .catch(console.error)
32
+ .finally(() => setLoading(false));
33
+ }, []);
34
+
35
+ // Connect to server when selected
36
+ const connectServer = useCallback(async (serverName: string) => {
37
+ if (!serverName) {
38
+ setTools([]);
39
+ setSelectedTool(null);
40
+ setError(null);
41
+ return;
42
+ }
43
+
44
+ setConnecting(true);
45
+ setError(null);
46
+ setTools([]);
47
+ setSelectedTool(null);
48
+ setToolResult(null);
49
+
50
+ try {
51
+ const res = await fetch(`/mcp/${serverName}`, {
52
+ method: 'POST',
53
+ headers: { 'Content-Type': 'application/json' },
54
+ body: JSON.stringify({
55
+ jsonrpc: '2.0',
56
+ id: 1,
57
+ method: 'tools/list',
58
+ params: {},
59
+ }),
60
+ });
61
+
62
+ const data = await res.json();
63
+ if (data.error) {
64
+ throw new Error(data.error.message || 'Failed to list tools');
65
+ }
66
+ setTools(data.result?.tools || []);
67
+ } catch (e) {
68
+ setError(e instanceof Error ? e.message : 'Connection failed');
69
+ } finally {
70
+ setConnecting(false);
71
+ }
72
+ }, []);
73
+
74
+ // Handle server selection
75
+ const handleServerChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
76
+ const serverName = e.target.value;
77
+ setSelectedServer(serverName);
78
+ connectServer(serverName);
79
+ };
80
+
81
+ // Execute a tool
82
+ const executeTool = useCallback(async () => {
83
+ if (!selectedServer || !selectedTool) return;
84
+
85
+ setExecuting(true);
86
+ setToolResult(null);
87
+
88
+ try {
89
+ const args = JSON.parse(toolInput);
90
+
91
+ const res = await fetch(`/mcp/${selectedServer}`, {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify({
95
+ jsonrpc: '2.0',
96
+ id: 2,
97
+ method: 'tools/call',
98
+ params: {
99
+ name: selectedTool.name,
100
+ arguments: args,
101
+ },
102
+ }),
103
+ });
104
+
105
+ const data = await res.json();
106
+ setToolResult(JSON.stringify(data, null, 2));
107
+ } catch (e) {
108
+ setToolResult(`Error: ${e instanceof Error ? e.message : 'Execution failed'}`);
109
+ } finally {
110
+ setExecuting(false);
111
+ }
112
+ }, [selectedServer, selectedTool, toolInput]);
113
+
114
+ // Generate default input from schema
115
+ const generateDefaultInput = useCallback((tool: McpTool) => {
116
+ if (!tool.inputSchema?.properties) return '{}';
117
+ const defaultObj: Record<string, unknown> = {};
118
+ for (const [key, prop] of Object.entries(tool.inputSchema.properties)) {
119
+ if (prop.type === 'string') defaultObj[key] = '';
120
+ else if (prop.type === 'number' || prop.type === 'integer') defaultObj[key] = 0;
121
+ else if (prop.type === 'boolean') defaultObj[key] = false;
122
+ else if (prop.type === 'array') defaultObj[key] = [];
123
+ else if (prop.type === 'object') defaultObj[key] = {};
124
+ }
125
+ return JSON.stringify(defaultObj, null, 2);
126
+ }, []);
127
+
128
+ // Handle tool selection
129
+ const handleToolSelect = (tool: McpTool) => {
130
+ setSelectedTool(tool);
131
+ setToolInput(generateDefaultInput(tool));
132
+ setToolResult(null);
133
+ };
134
+
135
+ if (loading) {
136
+ return (
137
+ <div className='flex justify-center items-center p-8'>
138
+ <span className='loading loading-spinner loading-lg' />
139
+ </div>
140
+ );
141
+ }
142
+
143
+ return (
144
+ <div className='flex flex-col h-full min-h-[600px]'>
145
+ {/* Header with server dropdown */}
146
+ <div className='p-3 border-b border-base-300 bg-base-100 flex items-center gap-4'>
147
+ <h3 className='font-semibold'>MCP Inspector</h3>
148
+ <select className='select select-bordered select-sm w-64' value={selectedServer} onChange={handleServerChange}>
149
+ <option value=''>Select a server...</option>
150
+ {servers.map((server) => (
151
+ <option key={server.name} value={server.name}>
152
+ {server.name} ({server.type})
153
+ </option>
154
+ ))}
155
+ </select>
156
+ {connecting && <span className='loading loading-spinner loading-sm' />}
157
+ {error && <span className='text-error text-sm'>{error}</span>}
158
+ </div>
159
+
160
+ {/* Main content */}
161
+ <div className='flex flex-1 overflow-hidden'>
162
+ {/* Tools List */}
163
+ <div className='w-64 border-r border-base-300 bg-base-100 flex flex-col flex-shrink-0'>
164
+ <div className='p-3 border-b border-base-300'>
165
+ <h4 className='font-medium text-sm'>Tools {tools.length > 0 && `(${tools.length})`}</h4>
166
+ </div>
167
+ <div className='flex-1 overflow-y-auto'>
168
+ {!selectedServer ? (
169
+ <div className='p-4 text-center text-base-content/50 text-sm'>Select a server above</div>
170
+ ) : tools.length === 0 && !connecting ? (
171
+ <div className='p-4 text-center text-base-content/50 text-sm'>No tools available</div>
172
+ ) : (
173
+ <ul className='menu p-2 gap-1'>
174
+ {tools.map((tool) => (
175
+ <li key={tool.name}>
176
+ <button
177
+ type='button'
178
+ className={`flex flex-col items-start text-left ${selectedTool?.name === tool.name ? 'active' : ''}`}
179
+ onClick={() => handleToolSelect(tool)}
180
+ >
181
+ <span className='font-mono text-xs'>{tool.name}</span>
182
+ {tool.description && (
183
+ <span className='text-xs text-base-content/50 truncate w-full'>{tool.description}</span>
184
+ )}
185
+ </button>
186
+ </li>
187
+ ))}
188
+ </ul>
189
+ )}
190
+ </div>
191
+ </div>
192
+
193
+ {/* Tool Execution Panel */}
194
+ <div className='flex-1 flex flex-col overflow-hidden'>
195
+ {!selectedTool ? (
196
+ <div className='flex-1 flex items-center justify-center text-base-content/50'>Select a tool to execute</div>
197
+ ) : (
198
+ <>
199
+ {/* Tool info */}
200
+ <div className='p-4 border-b border-base-300 bg-base-100'>
201
+ <h3 className='font-semibold mb-2'>{selectedTool.name}</h3>
202
+ {selectedTool.description && <p className='text-sm mb-3'>{selectedTool.description}</p>}
203
+ {selectedTool.inputSchema?.properties && (
204
+ <div className='text-xs text-base-content/70'>
205
+ <strong>Parameters:</strong>
206
+ <ul className='ml-4 mt-1 space-y-0.5'>
207
+ {Object.entries(selectedTool.inputSchema.properties).map(([name, prop]) => (
208
+ <li key={name}>
209
+ <code className='bg-base-200 px-1 rounded'>{name}</code>
210
+ <span className='ml-1 text-base-content/50'>({prop.type || 'any'})</span>
211
+ {selectedTool.inputSchema?.required?.includes(name) && (
212
+ <span className='text-error ml-1'>*</span>
213
+ )}
214
+ {prop.description && <span className='ml-2 text-base-content/60'>- {prop.description}</span>}
215
+ </li>
216
+ ))}
217
+ </ul>
218
+ </div>
219
+ )}
220
+ </div>
221
+
222
+ {/* Input */}
223
+ <div className='p-4 border-b border-base-300 bg-base-200'>
224
+ <label className='text-sm font-medium mb-2 block'>Arguments (JSON):</label>
225
+ <textarea
226
+ className='textarea textarea-bordered w-full font-mono text-xs'
227
+ rows={5}
228
+ value={toolInput}
229
+ onChange={(e) => setToolInput(e.target.value)}
230
+ />
231
+ <button
232
+ type='button'
233
+ className='btn btn-primary btn-sm mt-2'
234
+ onClick={executeTool}
235
+ disabled={executing}
236
+ >
237
+ {executing ? <span className='loading loading-spinner loading-sm' /> : 'Execute'}
238
+ </button>
239
+ </div>
240
+
241
+ {/* Result */}
242
+ <div className='flex-1 overflow-auto p-4 bg-base-200'>
243
+ <label className='text-sm font-medium mb-2 block'>Result:</label>
244
+ <pre className='bg-base-100 p-4 rounded-box text-xs overflow-auto max-h-[400px] border border-base-300'>
245
+ {toolResult || 'No result yet'}
246
+ </pre>
247
+ </div>
248
+ </>
249
+ )}
250
+ </div>
251
+ </div>
252
+ </div>
253
+ );
254
+ }
@@ -0,0 +1,139 @@
1
+ import { useEffect, useState } from 'react';
2
+ import type { ServerInfo, ToolInfo } from '../contracts';
3
+
4
+ export function ServersPage() {
5
+ const [servers, setServers] = useState<ServerInfo[]>([]);
6
+ const [tools, setTools] = useState<ToolInfo[]>([]);
7
+ const [selectedServer, setSelectedServer] = useState<string | null>(null);
8
+ const [loading, setLoading] = useState(true);
9
+ const [error, setError] = useState<string | null>(null);
10
+
11
+ useEffect(() => {
12
+ Promise.all([fetch('/api/mcps/servers').then((r) => r.json()), fetch('/api/mcps/tools').then((r) => r.json())])
13
+ .then(([serversData, toolsData]) => {
14
+ setServers(serversData.servers || []);
15
+ setTools(toolsData.tools || []);
16
+ })
17
+ .catch((e) => setError(e.message))
18
+ .finally(() => setLoading(false));
19
+ }, []);
20
+
21
+ const filteredTools = selectedServer ? tools.filter((t) => t.serverName === selectedServer) : tools;
22
+
23
+ if (loading) {
24
+ return (
25
+ <div className='flex justify-center items-center p-8'>
26
+ <span className='loading loading-spinner loading-lg' />
27
+ </div>
28
+ );
29
+ }
30
+
31
+ if (error) {
32
+ return (
33
+ <div className='alert alert-error'>
34
+ <span>{error}</span>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ return (
40
+ <div className='space-y-6'>
41
+ {/* Server List */}
42
+ <div>
43
+ <h2 className='text-lg font-semibold mb-3'>MCP Servers ({servers.length})</h2>
44
+ <div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4'>
45
+ {servers.map((server) => (
46
+ <div
47
+ key={server.name}
48
+ className={`card bg-base-100 shadow-sm border cursor-pointer transition-all ${
49
+ selectedServer === server.name ? 'border-primary ring-2 ring-primary/30' : 'border-base-300'
50
+ }`}
51
+ onClick={() => setSelectedServer(selectedServer === server.name ? null : server.name)}
52
+ onKeyDown={(e) =>
53
+ e.key === 'Enter' && setSelectedServer(selectedServer === server.name ? null : server.name)
54
+ }
55
+ role='button'
56
+ tabIndex={0}
57
+ >
58
+ <div className='card-body p-4'>
59
+ <div className='flex justify-between items-start'>
60
+ <div>
61
+ <div className='font-semibold'>{server.name}</div>
62
+ <span className={`badge badge-sm ${getServerTypeBadgeClass(server.type)}`}>{server.type}</span>
63
+ </div>
64
+ {server.disabled && <span className='badge badge-error badge-sm'>disabled</span>}
65
+ </div>
66
+ </div>
67
+ </div>
68
+ ))}
69
+ </div>
70
+ </div>
71
+
72
+ {/* Tools List */}
73
+ <div>
74
+ <div className='flex justify-between items-center mb-3'>
75
+ <h2 className='text-lg font-semibold'>
76
+ Tools {selectedServer ? `(${selectedServer})` : ''} ({filteredTools.length})
77
+ </h2>
78
+ {selectedServer && (
79
+ <button type='button' className='btn btn-ghost btn-xs' onClick={() => setSelectedServer(null)}>
80
+ Show all
81
+ </button>
82
+ )}
83
+ </div>
84
+
85
+ {filteredTools.length === 0 ? (
86
+ <div className='card bg-base-100 shadow-sm border border-base-300'>
87
+ <div className='card-body text-center text-base-content/50'>
88
+ <p>No tools available.</p>
89
+ <p className='text-sm'>
90
+ Tools will appear here when MCP servers are connected and their tools are listed.
91
+ </p>
92
+ </div>
93
+ </div>
94
+ ) : (
95
+ <div className='overflow-x-auto bg-base-100 rounded-box shadow-sm border border-base-300'>
96
+ <table className='table table-zebra'>
97
+ <thead>
98
+ <tr>
99
+ <th>Tool</th>
100
+ <th>Server</th>
101
+ <th>Description</th>
102
+ <th>Schema</th>
103
+ </tr>
104
+ </thead>
105
+ <tbody>
106
+ {filteredTools.map((tool) => (
107
+ <tr key={`${tool.serverName}/${tool.name}`}>
108
+ <td>
109
+ <span className='font-mono text-sm'>{tool.name}</span>
110
+ </td>
111
+ <td>
112
+ <span className='badge badge-sm'>{tool.serverName}</span>
113
+ </td>
114
+ <td className='text-sm text-base-content/70'>{tool.description || '-'}</td>
115
+ <td>
116
+ {tool.inputSchemaCompact && (
117
+ <code className='text-xs bg-base-200 px-1 py-0.5 rounded'>{tool.inputSchemaCompact}</code>
118
+ )}
119
+ </td>
120
+ </tr>
121
+ ))}
122
+ </tbody>
123
+ </table>
124
+ </div>
125
+ )}
126
+ </div>
127
+ </div>
128
+ );
129
+ }
130
+
131
+ function getServerTypeBadgeClass(type: string) {
132
+ const classes: Record<string, string> = {
133
+ 'tencent-cls': 'badge-info',
134
+ sql: 'badge-success',
135
+ prometheus: 'badge-secondary',
136
+ relay: 'badge-warning',
137
+ };
138
+ return classes[type] || 'badge-primary';
139
+ }