@loop_ouroboros/mcp-hub-lite 1.1.0 → 1.2.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/README.md +74 -116
  3. package/dist/client/assets/{HomeView-Bu2joUvW.js → HomeView-BBwvy1oj.js} +1 -1
  4. package/dist/client/assets/{ResourceDetailView-BvrhDCD1.js → ResourceDetailView-CZ2aB73w.js} +1 -1
  5. package/dist/client/assets/{ResourcesView-LjqioF_s.js → ResourcesView-CN1NlhWs.js} +1 -1
  6. package/dist/client/assets/{ServerDashboard-FhHJFvUi.js → ServerDashboard-k652Vw4Z.js} +1 -1
  7. package/dist/client/assets/{ServerDetail-BKV-M4qT.js → ServerDetail-BLQ-a4cO.js} +1 -1
  8. package/dist/client/assets/{ServerListView-BXgtDyt3.js → ServerListView-BHrsFD5i.js} +4 -4
  9. package/dist/client/assets/{ServerStatusTags.vue_vue_type_script_setup_true_lang-D-ooYNdN.js → ServerStatusTags.vue_vue_type_script_setup_true_lang-BHhwEuGe.js} +1 -1
  10. package/dist/client/assets/{SettingsView-CMFG91Z4.js → SettingsView-CUOFNXrz.js} +1 -1
  11. package/dist/client/assets/{ToolCallDialog-Bf4Xe4gH.js → ToolCallDialog-BfPjLxfV.js} +1 -1
  12. package/dist/client/assets/ToolsView-BxgXvPC3.css +1 -0
  13. package/dist/client/assets/ToolsView-CyuhYAE2.js +1 -0
  14. package/dist/client/assets/{_baseClone-Bp9Rjwd7.js → _baseClone-DO5qfalW.js} +1 -1
  15. package/dist/client/assets/{el-form-item-DdSUWYsl.js → el-form-item-CcGsD2K_.js} +2 -2
  16. package/dist/client/assets/{el-input-99gMrutP.js → el-input-tYgeiaCT.js} +1 -1
  17. package/dist/client/assets/{el-loading-CIQ5pD5u.js → el-loading-Dwl9E_Vr.js} +1 -1
  18. package/dist/client/assets/{el-overlay-BVM6msGX.js → el-overlay-kqX_BABo.js} +1 -1
  19. package/dist/client/assets/{el-radio-group-DhXWy7ry.js → el-radio-group-D8aWBVOT.js} +1 -1
  20. package/dist/client/assets/el-skeleton-item-BRwIFspE.js +1 -0
  21. package/dist/client/assets/{el-switch-Bu8AQ5uM.js → el-switch-BF8c-xeU.js} +1 -1
  22. package/dist/client/assets/{el-tab-pane-BnGMaV56.js → el-tab-pane-C4Ep94cd.js} +1 -1
  23. package/dist/client/assets/{el-table-column-BMWOaLS_.js → el-table-column-Cog6uCh-.js} +1 -1
  24. package/dist/client/assets/{index-C2V-ZGji.js → index-ByNBhPAR.js} +1 -1
  25. package/dist/client/assets/index-CTB6oe-9.js +2 -0
  26. package/dist/client/assets/omit-CUnDT6sS.js +1 -0
  27. package/dist/client/assets/{raf-C2wXzaVU.js → raf-CmzeRPMd.js} +1 -1
  28. package/dist/client/assets/{vue-vendor-BLHLXXJK.js → vue-vendor-CbgVSHIh.js} +3 -3
  29. package/dist/client/index.html +2 -2
  30. package/dist/server/src/api/mcp/debug-response-wrapper.js +2 -2
  31. package/dist/server/src/api/mcp/gateway.d.ts.map +1 -1
  32. package/dist/server/src/api/mcp/gateway.js +17 -3
  33. package/dist/server/src/api/mcp/session-context-extractor.d.ts.map +1 -1
  34. package/dist/server/src/api/mcp/session-context-extractor.js +14 -5
  35. package/dist/server/src/cli/commands/mcp-tool-use.d.ts +62 -0
  36. package/dist/server/src/cli/commands/mcp-tool-use.d.ts.map +1 -0
  37. package/dist/server/src/cli/commands/mcp-tool-use.js +174 -0
  38. package/dist/server/src/cli/commands/server.d.ts +57 -0
  39. package/dist/server/src/cli/commands/server.d.ts.map +1 -0
  40. package/dist/server/src/cli/commands/server.js +169 -0
  41. package/dist/server/src/cli/commands/tool-use.d.ts +95 -0
  42. package/dist/server/src/cli/commands/tool-use.d.ts.map +1 -0
  43. package/dist/server/src/cli/commands/tool-use.js +255 -0
  44. package/dist/server/src/cli/index.d.ts +4 -2
  45. package/dist/server/src/cli/index.d.ts.map +1 -1
  46. package/dist/server/src/cli/index.js +35 -3
  47. package/dist/server/src/config/config-loader.js +1 -1
  48. package/dist/server/src/config/config-manager.js +1 -1
  49. package/dist/server/src/models/system-tools.constants.d.ts +2 -2
  50. package/dist/server/src/models/system-tools.constants.d.ts.map +1 -1
  51. package/dist/server/src/models/system-tools.constants.js +2 -2
  52. package/dist/server/src/services/connection/tool-cache.d.ts.map +1 -1
  53. package/dist/server/src/services/connection/tool-cache.js +3 -1
  54. package/dist/server/src/services/gateway/gateway.service.d.ts.map +1 -1
  55. package/dist/server/src/services/gateway/gateway.service.js +38 -4
  56. package/dist/server/src/services/gateway/global-transport.d.ts +14 -5
  57. package/dist/server/src/services/gateway/global-transport.d.ts.map +1 -1
  58. package/dist/server/src/services/gateway/global-transport.js +54 -30
  59. package/dist/server/src/services/gateway/request-handlers/initialize-handler.d.ts.map +1 -1
  60. package/dist/server/src/services/gateway/request-handlers/initialize-handler.js +12 -1
  61. package/dist/server/src/services/gateway/request-handlers/system-tools-handler.js +2 -2
  62. package/dist/server/src/services/gateway/request-handlers/tools-handler.d.ts.map +1 -1
  63. package/dist/server/src/services/gateway/request-handlers/tools-handler.js +3 -4
  64. package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts +1 -0
  65. package/dist/server/src/services/hub-tools/system-tool-definitions.d.ts.map +1 -1
  66. package/dist/server/src/services/hub-tools/system-tool-definitions.js +4 -3
  67. package/dist/server/src/services/hub-tools.service.d.ts +3 -2
  68. package/dist/server/src/services/hub-tools.service.d.ts.map +1 -1
  69. package/dist/server/src/services/hub-tools.service.js +36 -19
  70. package/dist/server/src/services/search/search-core.service.d.ts +5 -5
  71. package/dist/server/src/services/search/search-core.service.js +11 -11
  72. package/dist/server/src/services/session/session-manager.d.ts +256 -12
  73. package/dist/server/src/services/session/session-manager.d.ts.map +1 -1
  74. package/dist/server/src/services/session/session-manager.js +585 -23
  75. package/dist/server/src/services/session-tracker.service.d.ts +10 -2
  76. package/dist/server/src/services/session-tracker.service.d.ts.map +1 -1
  77. package/dist/server/src/services/session-tracker.service.js +53 -2
  78. package/dist/server/src/services/system-tool-handler.js +2 -2
  79. package/dist/server/src/utils/index.d.ts +1 -0
  80. package/dist/server/src/utils/index.d.ts.map +1 -1
  81. package/dist/server/src/utils/index.js +1 -0
  82. package/dist/server/src/utils/name-converter.d.ts +17 -0
  83. package/dist/server/src/utils/name-converter.d.ts.map +1 -0
  84. package/dist/server/src/utils/name-converter.js +27 -0
  85. package/dist/server/src/utils/request-context.d.ts +18 -0
  86. package/dist/server/src/utils/request-context.d.ts.map +1 -1
  87. package/dist/server/src/utils/request-context.js +20 -0
  88. package/dist/server/tests/evaluation/evaluation.test.js +9 -10
  89. package/dist/server/tests/integration/api/gateway.test.js +2 -2
  90. package/dist/server/tests/unit/services/hub-tools.service.test.js +1 -1
  91. package/dist/server/tests/unit/utils/name-converter.test.d.ts +2 -0
  92. package/dist/server/tests/unit/utils/name-converter.test.d.ts.map +1 -0
  93. package/dist/server/tests/unit/utils/name-converter.test.js +69 -0
  94. package/dist/server/tests/unit/utils/request-context.test.js +24 -5
  95. package/package.json +3 -1
  96. package/dist/client/assets/ToolsView-DFpha1z0.js +0 -1
  97. package/dist/client/assets/ToolsView-E3Ps9c7i.css +0 -1
  98. package/dist/client/assets/el-skeleton-item-DJz-Us12.js +0 -1
  99. package/dist/client/assets/index-vhkqgpmN.js +0 -2
  100. package/dist/client/assets/omit-CqPQN3XP.js +0 -1
@@ -47,6 +47,8 @@ declare class SessionTrackerService {
47
47
  * @param context.clientName - Optional name of the client application
48
48
  * @param context.clientVersion - Optional version of the client application
49
49
  * @param context.protocolVersion - Optional MCP protocol version
50
+ * @param context.project - Optional project identifier
51
+ * @param context.cwd - Optional current working directory
50
52
  * @param context.userAgent - Optional user agent string
51
53
  * @param context.ip - Optional session IP address
52
54
  *
@@ -54,7 +56,8 @@ declare class SessionTrackerService {
54
56
  * sessionTrackerService.updateSession({
55
57
  * sessionId: 'session-123',
56
58
  * clientName: 'VS Code',
57
- * clientVersion: '1.85.0'
59
+ * clientVersion: '1.85.0',
60
+ * cwd: '/home/user/project'
58
61
  * });
59
62
  */
60
63
  updateSession(context: SessionContext): void;
@@ -62,7 +65,12 @@ declare class SessionTrackerService {
62
65
  * Updates the workspace roots for a specific session.
63
66
  *
64
67
  * This method is typically called when a session provides its workspace root
65
- * directories.
68
+ * directories, which helps determine the session's working directory context.
69
+ * If the session doesn't have a current working directory (cwd) set, it will
70
+ * attempt to infer one from the first root URI in the list.
71
+ *
72
+ * For file:// URIs, it converts them to local file paths using Node.js's
73
+ * fileURLToPath utility. For other URI schemes, it uses the URI directly.
66
74
  *
67
75
  * @param sessionId - The unique session identifier for the session
68
76
  * @param roots - Array of session root objects containing URI information
@@ -1 +1 @@
1
- {"version":3,"file":"session-tracker.service.d.ts","sourceRoot":"","sources":["../../../../src/services/session-tracker.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,WAAW,EACZ,MAAM,wCAAwC,CAAC;AAMhD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,cAAM,qBAAqB;IACzB,OAAO,CAAC,QAAQ,CAAuC;IAEvD,OAAO,KAAK,UAAU,GAErB;IAED;;;;;OAKG;;IAMH;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACI,aAAa,CAAC,OAAO,EAAE,cAAc;IA0B5C;;;;;;;;;;;;;;OAcG;IACI,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE;IAQjE;;;;;;;;;;;;OAYG;IACI,WAAW,IAAI,WAAW,EAAE;IAInC;;;;;;;;;;;;;;OAcG;IACI,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI7D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,OAAO;CAkBhB;AAED,eAAO,MAAM,qBAAqB,uBAA8B,CAAC;AAEjE,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"session-tracker.service.d.ts","sourceRoot":"","sources":["../../../../src/services/session-tracker.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,WAAW,EACZ,MAAM,wCAAwC,CAAC;AAOhD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,cAAM,qBAAqB;IACzB,OAAO,CAAC,QAAQ,CAAuC;IAEvD,OAAO,KAAK,UAAU,GAErB;IAED;;;;;OAKG;;IAMH;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACI,aAAa,CAAC,OAAO,EAAE,cAAc;IA4B5C;;;;;;;;;;;;;;;;;;;OAmBG;IACI,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE;IAoDjE;;;;;;;;;;;;OAYG;IACI,WAAW,IAAI,WAAW,EAAE;IAInC;;;;;;;;;;;;;;OAcG;IACI,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI7D;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,OAAO;CAkBhB;AAED,eAAO,MAAM,qBAAqB,uBAA8B,CAAC;AAEjE,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { logger, LOG_MODULES } from '../utils/logger.js';
2
2
  import { eventBus } from './event-bus.service.js';
3
+ import { fileURLToPath } from 'node:url';
3
4
  import { configManager } from '../config/config-manager.js';
4
5
  import { formatDuration } from '../utils/format-utils.js';
5
6
  /**
@@ -56,6 +57,8 @@ class SessionTrackerService {
56
57
  * @param context.clientName - Optional name of the client application
57
58
  * @param context.clientVersion - Optional version of the client application
58
59
  * @param context.protocolVersion - Optional MCP protocol version
60
+ * @param context.project - Optional project identifier
61
+ * @param context.cwd - Optional current working directory
59
62
  * @param context.userAgent - Optional user agent string
60
63
  * @param context.ip - Optional session IP address
61
64
  *
@@ -63,7 +66,8 @@ class SessionTrackerService {
63
66
  * sessionTrackerService.updateSession({
64
67
  * sessionId: 'session-123',
65
68
  * clientName: 'VS Code',
66
- * clientVersion: '1.85.0'
69
+ * clientVersion: '1.85.0',
70
+ * cwd: '/home/user/project'
67
71
  * });
68
72
  */
69
73
  updateSession(context) {
@@ -75,6 +79,8 @@ class SessionTrackerService {
75
79
  clientVersion: context.clientVersion || existing?.clientVersion,
76
80
  protocolVersion: context.protocolVersion || existing?.protocolVersion,
77
81
  capabilities: context.capabilities || existing?.capabilities,
82
+ project: context.project || existing?.project,
83
+ cwd: context.cwd || existing?.cwd, // Preserve CWD if not provided in new request (e.g. inferred from roots)
78
84
  userAgent: context.userAgent || existing?.userAgent,
79
85
  ip: context.ip || existing?.ip,
80
86
  lastSeen: Date.now(),
@@ -93,7 +99,12 @@ class SessionTrackerService {
93
99
  * Updates the workspace roots for a specific session.
94
100
  *
95
101
  * This method is typically called when a session provides its workspace root
96
- * directories.
102
+ * directories, which helps determine the session's working directory context.
103
+ * If the session doesn't have a current working directory (cwd) set, it will
104
+ * attempt to infer one from the first root URI in the list.
105
+ *
106
+ * For file:// URIs, it converts them to local file paths using Node.js's
107
+ * fileURLToPath utility. For other URI schemes, it uses the URI directly.
97
108
  *
98
109
  * @param sessionId - The unique session identifier for the session
99
110
  * @param roots - Array of session root objects containing URI information
@@ -109,6 +120,46 @@ class SessionTrackerService {
109
120
  if (session) {
110
121
  session.roots = roots;
111
122
  session.lastSeen = Date.now();
123
+ // If CWD is missing, try to infer from roots
124
+ if (!session.cwd && roots.length > 0) {
125
+ // Convert file:// uri to path if possible, or just use URI
126
+ const root = roots[0];
127
+ if (root.uri.startsWith('file://')) {
128
+ try {
129
+ session.cwd = fileURLToPath(root.uri);
130
+ }
131
+ catch {
132
+ session.cwd = root.uri;
133
+ }
134
+ }
135
+ else {
136
+ session.cwd = root.uri;
137
+ }
138
+ logger.debug(`Inferred CWD for session ${sessionId} from roots: ${session.cwd}`, LOG_MODULES.SESSION_TRACKER);
139
+ }
140
+ // If project is missing, try to infer from roots (use name or last path segment)
141
+ if (!session.project && roots.length > 0) {
142
+ const root = roots[0];
143
+ if (root.name) {
144
+ session.project = root.name;
145
+ }
146
+ else if (root.uri.startsWith('file://')) {
147
+ try {
148
+ const localPath = fileURLToPath(root.uri);
149
+ const pathSegments = localPath.split(/[/\\]/);
150
+ const lastSegment = pathSegments.filter(Boolean).pop();
151
+ if (lastSegment) {
152
+ session.project = lastSegment;
153
+ }
154
+ }
155
+ catch {
156
+ // If path parsing fails, don't set project
157
+ }
158
+ }
159
+ if (session.project) {
160
+ logger.debug(`Inferred project for session ${sessionId} from roots: ${session.project}`, LOG_MODULES.SESSION_TRACKER);
161
+ }
162
+ }
112
163
  }
113
164
  }
114
165
  /**
@@ -1,7 +1,7 @@
1
1
  import { hubToolsService } from './hub-tools.service.js';
2
2
  import { McpError } from '@modelcontextprotocol/sdk/types.js';
3
3
  import { logger, LOG_MODULES } from '../utils/logger.js';
4
- import { LIST_SERVERS_TOOL, LIST_TOOLS_IN_SERVER_TOOL, GET_TOOL_TOOL, CALL_TOOL_TOOL, UPDATE_SERVER_DESCRIPTION_TOOL, MCP_HUB_LITE_SERVER } from '../models/system-tools.constants.js';
4
+ import { LIST_SERVERS_TOOL, LIST_TOOLS_TOOL, GET_TOOL_TOOL, CALL_TOOL_TOOL, UPDATE_SERVER_DESCRIPTION_TOOL, MCP_HUB_LITE_SERVER } from '../models/system-tools.constants.js';
5
5
  import { stringifyForLogging } from '../utils/json-utils.js';
6
6
  /**
7
7
  * Unified system tool call handler
@@ -18,7 +18,7 @@ export class SystemToolHandler {
18
18
  case LIST_SERVERS_TOOL:
19
19
  result = await hubToolsService.listServers();
20
20
  break;
21
- case LIST_TOOLS_IN_SERVER_TOOL: {
21
+ case LIST_TOOLS_TOOL: {
22
22
  const listToolsArgs = toolArgs;
23
23
  result = await hubToolsService.listToolsInServer(listToolsArgs);
24
24
  break;
@@ -1,4 +1,5 @@
1
1
  export { logger, LOG_MODULES } from './logger.js';
2
2
  export * from './sort-utils.js';
3
3
  export * from './version.js';
4
+ export { normalizeToolName } from './name-converter.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClD,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClD,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export { logger, LOG_MODULES } from './logger.js';
2
2
  export * from './sort-utils.js';
3
3
  export * from './version.js';
4
+ export { normalizeToolName } from './name-converter.js';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Normalizes a tool name to a standard format for comparison.
3
+ *
4
+ * This function converts various naming conventions (snake_case, kebab-case,
5
+ * camelCase, space-separated, or uppercase) into a normalized lowercase
6
+ * underscore format for consistent matching.
7
+ *
8
+ * @param toolName - The tool name to normalize
9
+ * @returns Normalized tool name in lowercase underscore format
10
+ * @example
11
+ * normalizeToolName('list-servers') // returns 'list_servers'
12
+ * normalizeToolName('LIST_SERVERS') // returns 'list_servers'
13
+ * normalizeToolName('chatCompletions') // returns 'chat_completions'
14
+ * normalizeToolName('chat_completions') // returns 'chat_completions'
15
+ */
16
+ export declare function normalizeToolName(toolName: string): string;
17
+ //# sourceMappingURL=name-converter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-converter.d.ts","sourceRoot":"","sources":["../../../../src/utils/name-converter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa1D"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Normalizes a tool name to a standard format for comparison.
3
+ *
4
+ * This function converts various naming conventions (snake_case, kebab-case,
5
+ * camelCase, space-separated, or uppercase) into a normalized lowercase
6
+ * underscore format for consistent matching.
7
+ *
8
+ * @param toolName - The tool name to normalize
9
+ * @returns Normalized tool name in lowercase underscore format
10
+ * @example
11
+ * normalizeToolName('list-servers') // returns 'list_servers'
12
+ * normalizeToolName('LIST_SERVERS') // returns 'list_servers'
13
+ * normalizeToolName('chatCompletions') // returns 'chat_completions'
14
+ * normalizeToolName('chat_completions') // returns 'chat_completions'
15
+ */
16
+ export function normalizeToolName(toolName) {
17
+ return (toolName
18
+ // Insert underscore before uppercase letters (for camelCase handling)
19
+ // e.g., 'chatCompletions' -> 'chat_Completions'
20
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
21
+ // Convert to lowercase
22
+ .toLowerCase()
23
+ // Replace hyphens, underscores, and spaces with single underscore
24
+ .replace(/[-_\s]+/g, '_')
25
+ // Remove leading and trailing underscores
26
+ .replace(/^_+|_+$/g, ''));
27
+ }
@@ -50,4 +50,22 @@ export declare const requestContext: AsyncLocalStorage<SessionContext>;
50
50
  * ```
51
51
  */
52
52
  export declare function getSessionContext(): SessionContext | undefined;
53
+ /**
54
+ * Retrieves the current request's working directory (cwd).
55
+ *
56
+ * This is a convenience function that extracts the cwd property from the
57
+ * current request's session context. It's commonly used in file operations
58
+ * that need to respect the session's current working directory.
59
+ *
60
+ * @returns {string | undefined} The current request's working directory, or undefined if not available
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const cwd = getSessionCwd();
65
+ * if (cwd) {
66
+ * const fullPath = path.join(cwd, relativePath);
67
+ * }
68
+ * ```
69
+ */
70
+ export declare function getSessionCwd(): string | undefined;
53
71
  //# sourceMappingURL=request-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../../../src/utils/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAE7E;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAEtE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D"}
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../../../src/utils/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAE7E;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAEtE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD"}
@@ -51,3 +51,23 @@ export const requestContext = new AsyncLocalStorage();
51
51
  export function getSessionContext() {
52
52
  return requestContext.getStore();
53
53
  }
54
+ /**
55
+ * Retrieves the current request's working directory (cwd).
56
+ *
57
+ * This is a convenience function that extracts the cwd property from the
58
+ * current request's session context. It's commonly used in file operations
59
+ * that need to respect the session's current working directory.
60
+ *
61
+ * @returns {string | undefined} The current request's working directory, or undefined if not available
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const cwd = getSessionCwd();
66
+ * if (cwd) {
67
+ * const fullPath = path.join(cwd, relativePath);
68
+ * }
69
+ * ```
70
+ */
71
+ export function getSessionCwd() {
72
+ return requestContext.getStore()?.cwd;
73
+ }
@@ -84,20 +84,19 @@ describe('MCP Evaluation Tests', () => {
84
84
  expect(hasDestructiveIndicator, `Answer ${index + 1} may contain destructive operations: "${answer}"`).toBe(false);
85
85
  });
86
86
  });
87
- it('should reference valid MCP tools', () => {
87
+ it('should reference valid tools', () => {
88
88
  const validTools = [
89
- 'list_servers',
90
- 'find_servers',
91
- 'list_all_tools_in_server',
92
- 'find_tools_in_server',
93
- 'get_tool',
94
- 'call_tool',
95
- 'find_tools'
89
+ 'list-servers',
90
+ 'find-servers',
91
+ 'list-all-tools-in-server',
92
+ 'find-tools-in-server',
93
+ 'get-tool',
94
+ 'call-tool',
95
+ 'find-tools'
96
96
  ];
97
97
  const content = fs.readFileSync(evaluationFile, 'utf-8');
98
- const normalizedContent = content.replace(/\b[a-z]+(?:-[a-z]+)+\b/g, (match) => match.replace(/-/g, '_'));
99
98
  validTools.forEach((tool) => {
100
- expect(normalizedContent).toContain(tool);
99
+ expect(content).toContain(tool);
101
100
  });
102
101
  });
103
102
  });
@@ -127,7 +127,7 @@ describe('GatewayService', () => {
127
127
  vi.mocked(mocks.getServerById).mockReturnValue({
128
128
  name: 'Test Server',
129
129
  id: 'server1',
130
- config: { template: { aggregatedTools: ['testTool'] } },
130
+ config: { allowedTools: ['testTool'] },
131
131
  instance: { id: 'server1', timestamp: Date.now(), hash: 'abc123' }
132
132
  });
133
133
  // Populate toolCache for the test
@@ -170,7 +170,7 @@ describe('GatewayService', () => {
170
170
  vi.mocked(mocks.getServerById).mockReturnValue({
171
171
  name: 'Test Server',
172
172
  id: 'server1',
173
- config: { template: { aggregatedTools: ['testTool'] } },
173
+ config: { allowedTools: ['testTool'] },
174
174
  instance: { id: 'server1', timestamp: Date.now(), hash: 'abc123' }
175
175
  });
176
176
  // Find list tools handler to populate tool map
@@ -613,7 +613,7 @@ describe('HubToolsService', () => {
613
613
  // Assert system tools - should have 5 tools now
614
614
  const systemToolNames = allTools['mcp-hub-lite'].tools.map((t) => t.name);
615
615
  expect(systemToolNames).toContain('list_servers');
616
- expect(systemToolNames).toContain('list_tools_in_server');
616
+ expect(systemToolNames).toContain('list_tools');
617
617
  expect(systemToolNames).toContain('get_tool');
618
618
  expect(systemToolNames).toContain('call_tool');
619
619
  expect(systemToolNames).toContain('update_server_description');
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=name-converter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-converter.test.d.ts","sourceRoot":"","sources":["../../../../../tests/unit/utils/name-converter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { normalizeToolName } from '../../../src/utils/name-converter.js';
3
+ describe('name-converter', () => {
4
+ describe('normalizeToolName', () => {
5
+ it('should convert kebab-case to snake_case', () => {
6
+ expect(normalizeToolName('list-servers')).toBe('list_servers');
7
+ expect(normalizeToolName('get-tool')).toBe('get_tool');
8
+ expect(normalizeToolName('call-tool')).toBe('call_tool');
9
+ });
10
+ it('should convert uppercase to lowercase', () => {
11
+ expect(normalizeToolName('LIST_SERVERS')).toBe('list_servers');
12
+ expect(normalizeToolName('GET_TOOL')).toBe('get_tool');
13
+ expect(normalizeToolName('LIST_SERVERS_TOOL')).toBe('list_servers_tool');
14
+ });
15
+ it('should convert mixed case to lowercase with underscores', () => {
16
+ expect(normalizeToolName('ListServers')).toBe('list_servers');
17
+ expect(normalizeToolName('GetTool')).toBe('get_tool');
18
+ expect(normalizeToolName('chatCompletions')).toBe('chat_completions');
19
+ });
20
+ it('should convert space-separated to underscores', () => {
21
+ expect(normalizeToolName('list servers')).toBe('list_servers');
22
+ expect(normalizeToolName('get tool info')).toBe('get_tool_info');
23
+ });
24
+ it('should handle already normalized names', () => {
25
+ expect(normalizeToolName('list_servers')).toBe('list_servers');
26
+ expect(normalizeToolName('get_tool')).toBe('get_tool');
27
+ expect(normalizeToolName('chat_completions')).toBe('chat_completions');
28
+ });
29
+ it('should remove leading and trailing underscores', () => {
30
+ expect(normalizeToolName('_list_servers_')).toBe('list_servers');
31
+ expect(normalizeToolName('__get_tool__')).toBe('get_tool');
32
+ });
33
+ it('should handle multiple consecutive separators', () => {
34
+ expect(normalizeToolName('list--servers')).toBe('list_servers');
35
+ expect(normalizeToolName('list__servers')).toBe('list_servers');
36
+ expect(normalizeToolName('list servers')).toBe('list_servers');
37
+ expect(normalizeToolName('list-_-servers')).toBe('list_servers');
38
+ });
39
+ it('should handle empty string', () => {
40
+ expect(normalizeToolName('')).toBe('');
41
+ });
42
+ it('should handle single character', () => {
43
+ expect(normalizeToolName('a')).toBe('a');
44
+ expect(normalizeToolName('A')).toBe('a');
45
+ });
46
+ it('should handle names with numbers', () => {
47
+ expect(normalizeToolName('tool123')).toBe('tool123');
48
+ expect(normalizeToolName('tool_123')).toBe('tool_123');
49
+ expect(normalizeToolName('tool-123')).toBe('tool_123');
50
+ });
51
+ });
52
+ describe('normalization consistency', () => {
53
+ it('should normalize different formats to the same value for same base name', () => {
54
+ // All these should normalize to the same value
55
+ const normalized = normalizeToolName('list_servers');
56
+ expect(normalizeToolName('list-servers')).toBe(normalized);
57
+ expect(normalizeToolName('LIST_SERVERS')).toBe(normalized);
58
+ expect(normalizeToolName('list_servers')).toBe(normalized);
59
+ expect(normalizeToolName('List_Servers')).toBe(normalized);
60
+ });
61
+ it('should convert camelCase to snake_case', () => {
62
+ // Note: camelCase normalization properly handles uppercase boundaries
63
+ // e.g., 'chatCompletions' -> 'chat_Completions' -> 'chat_completions'
64
+ expect(normalizeToolName('chatCompletions')).toBe('chat_completions');
65
+ expect(normalizeToolName('getTool')).toBe('get_tool');
66
+ expect(normalizeToolName('listServers')).toBe('list_servers');
67
+ });
68
+ });
69
+ });
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { requestContext, getSessionContext } from '../../../src/utils/request-context.js';
2
+ import { requestContext, getSessionContext, getSessionCwd } from '../../../src/utils/request-context.js';
3
3
  describe('Request Context', () => {
4
4
  beforeEach(() => {
5
5
  // Clean up any existing context
@@ -11,11 +11,14 @@ describe('Request Context', () => {
11
11
  });
12
12
  it('should return undefined when no context is set', () => {
13
13
  expect(getSessionContext()).toBeUndefined();
14
+ expect(getSessionCwd()).toBeUndefined();
14
15
  });
15
16
  it('should store and retrieve session context correctly', async () => {
16
17
  const testContext = {
17
18
  sessionId: 'test-session',
18
19
  clientName: 'Test Client',
20
+ cwd: '/test/cwd',
21
+ project: 'test-project',
19
22
  ip: '127.0.0.1',
20
23
  userAgent: 'test-agent',
21
24
  timestamp: Date.now()
@@ -23,6 +26,8 @@ describe('Request Context', () => {
23
26
  await requestContext.run(testContext, () => {
24
27
  const context = getSessionContext();
25
28
  expect(context).toEqual(testContext);
29
+ const cwd = getSessionCwd();
30
+ expect(cwd).toBe('/test/cwd');
26
31
  });
27
32
  });
28
33
  it('should handle partial context correctly', async () => {
@@ -33,15 +38,19 @@ describe('Request Context', () => {
33
38
  await requestContext.run(partialContext, () => {
34
39
  const context = getSessionContext();
35
40
  expect(context).toEqual(partialContext);
41
+ const cwd = getSessionCwd();
42
+ expect(cwd).toBeUndefined();
36
43
  });
37
44
  });
38
45
  it('should maintain context isolation between async operations', async () => {
39
46
  const context1 = {
40
47
  sessionId: 'session-1',
48
+ cwd: '/path/1',
41
49
  timestamp: Date.now()
42
50
  };
43
51
  const context2 = {
44
52
  sessionId: 'session-2',
53
+ cwd: '/path/2',
45
54
  timestamp: Date.now()
46
55
  };
47
56
  let result1;
@@ -49,15 +58,25 @@ describe('Request Context', () => {
49
58
  const operation1 = requestContext.run(context1, async () => {
50
59
  // Simulate async operation
51
60
  await new Promise((resolve) => setTimeout(resolve, 10));
52
- result1 = getSessionContext();
61
+ result1 = getSessionCwd();
53
62
  });
54
63
  const operation2 = requestContext.run(context2, async () => {
55
64
  // Simulate async operation
56
65
  await new Promise((resolve) => setTimeout(resolve, 5));
57
- result2 = getSessionContext();
66
+ result2 = getSessionCwd();
58
67
  });
59
68
  await Promise.all([operation1, operation2]);
60
- expect(result1).toEqual(context1);
61
- expect(result2).toEqual(context2);
69
+ expect(result1).toBe('/path/1');
70
+ expect(result2).toBe('/path/2');
71
+ });
72
+ it('should return undefined for getSessionCwd when cwd is not set', async () => {
73
+ const contextWithoutCwd = {
74
+ sessionId: 'no-cwd-session',
75
+ timestamp: Date.now()
76
+ };
77
+ await requestContext.run(contextWithoutCwd, () => {
78
+ const cwd = getSessionCwd();
79
+ expect(cwd).toBeUndefined();
80
+ });
62
81
  });
63
82
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loop_ouroboros/mcp-hub-lite",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A lightweight MCP management platform designed for independent developers",
5
5
  "license": "MIT",
6
6
  "author": "loop_ouroboros",
@@ -41,6 +41,7 @@
41
41
  "status": "node dist/server/src/cli/index.js status",
42
42
  "list": "node dist/server/src/cli/index.js list",
43
43
  "ui": "node dist/server/src/cli/index.js ui",
44
+ "tool-use": "node dist/server/src/cli/index.js tool-use",
44
45
  "dev": "run-p dev:client dev:server",
45
46
  "dev:client": "vite",
46
47
  "dev:server": "tsx src/server/dev-server.ts",
@@ -73,6 +74,7 @@
73
74
  "fastify": "^5.6.2",
74
75
  "open": "^11.0.0",
75
76
  "pinia": "^3.0.4",
77
+ "undici": "^8.1.0",
76
78
  "vue": "^3.5.26",
77
79
  "vue-i18n": "^11.2.7",
78
80
  "vue-router": "^4.6.4",
@@ -1 +0,0 @@
1
- import{u as H,R as J,E as M,k as B,h as Q,S as K,U as z,s as W,V as X}from"./index-vhkqgpmN.js";import{E as Y}from"./el-skeleton-item-DJz-Us12.js";import{E as Z}from"./el-input-99gMrutP.js";import{t as O,A as i,C as t,U as P,J as E,$ as n,y as a,a6 as ee,r as m,c as te,o as se,x as T,O as l,G as y,u as g,H as F,a9 as R,L as j,P as S,af as V}from"./vue-vendor-BLHLXXJK.js";import{T as oe}from"./ToolCallDialog-Bf4Xe4gH.js";import{_ as ae}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./typescript-Bp3YSIOJ.js";import"./event-BB_Ol6Sd.js";import"./el-overlay-BVM6msGX.js";import"./index-C2V-ZGji.js";const le={class:"min-w-0 mb-3"},ne={class:"flex items-center gap-2 mb-2"},re=["title"],ie=["title"],L=O({__name:"ToolCard",props:{title:{},description:{},tagName:{},tagClass:{}},emits:["call"],setup(c){return(f,v)=>(a(),i("div",{class:"bg-white dark:bg-[#1e293b] border border-gray-200 dark:border-gray-700 rounded-lg p-4 flex flex-col justify-between shadow-sm hover:shadow-md transition-shadow h-full cursor-pointer",onClick:v[0]||(v[0]=_=>f.$emit("call"))},[t("div",le,[t("div",ne,[c.tagName?(a(),i("span",{key:0,class:E(["shrink-0 px-2 py-0.5 rounded-full text-xs font-medium whitespace-nowrap",c.tagClass||"bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300"])},n(c.tagName),3)):P("",!0),t("span",{class:"font-bold text-gray-900 dark:text-gray-100 font-mono text-base break-all",title:c.title},n(c.title),9,re)]),t("p",{class:"text-sm text-gray-600 dark:text-gray-400 line-clamp-2",title:c.description},n(c.description||f.$t("tools.noDescription")),9,ie)])]))}}),ce={class:"tools-view py-6 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto w-full h-full flex flex-col overflow-hidden bg-gray-50 dark:bg-[#0f172a] transition-colors duration-300"},de={class:"mb-6 shrink-0"},ue={class:"text-2xl font-semibold text-gray-900 dark:text-white mb-4"},me={class:"relative"},ve={class:"flex-1 overflow-y-auto custom-scrollbar space-y-8 pr-2"},pe={class:"text-lg font-bold text-gray-900 dark:text-white"},ge={class:"text-sm font-normal text-gray-500 ml-2"},fe={class:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8 transition-all duration-300"},he={class:"flex items-center gap-2 mb-4"},xe={class:"text-lg font-bold text-gray-900 dark:text-white"},ye={class:"text-sm font-normal text-gray-500 ml-2"},_e={key:0,class:"space-y-4"},be={key:1,class:"text-center py-8 text-gray-500"},ke={key:2,class:"space-y-6"},we=["onClick"],Ce={class:"text-xs bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded-full"},$e={class:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 transition-all duration-300"},Ne=O({__name:"ToolsView",setup(c){const{t:f}=ee(),v=m(""),_=m(!1),D=m([]),k=H(),w=m([]),C=m(!1),p=m(null),h=m(new Set),b=m(!1);function q(e){h.value.has(e)?h.value.delete(e):h.value.add(e)}async function A(){try{const e=await B.get("/web/hub-tools/system");w.value=e.map(s=>({...s,serverName:"mcp-hub-lite"})).sort((s,d)=>s.name.localeCompare(d.name))}catch(e){console.error("Failed to fetch system tools:",e)}}function I(e){p.value={...e,serverName:e.serverName},C.value=!0}async function U(){_.value=!0;try{const e=await B.get(`/web/search?q=${encodeURIComponent(v.value)}`);D.value=e.results||[]}catch(e){console.error("Failed to fetch tools:",e)}finally{_.value=!1}}function G(){U()}const $=te(()=>{const e={};return D.value.forEach(s=>{const x=k.servers.find(N=>N.name===s.tool.serverName)?.status==="online"?f("tools.online"):f("tools.offline"),r=s.tool.serverName?`${s.tool.serverName} (${x})`:"Unknown";e[r]||(e[r]=[]),e[r].push(s)}),Object.entries(e).map(([s,d])=>({serverName:s,tools:d.sort((x,r)=>x.tool.name.localeCompare(r.tool.name))})).sort((s,d)=>s.serverName.localeCompare(d.serverName))});return se(()=>{A(),U(),k.servers.length===0&&k.fetchServers()}),(e,s)=>{const d=Q,x=Z,r=M,N=Y;return a(),i("div",ce,[t("div",de,[t("h2",ue,n(e.$t("tools.title")),1),t("div",me,[l(x,{modelValue:v.value,"onUpdate:modelValue":s[0]||(s[0]=o=>v.value=o),placeholder:e.$t("tools.searchPlaceholder"),class:"w-full",size:"large","prefix-icon":g(J),onInput:G},{append:y(()=>[l(d,{icon:g(K)},null,8,["icon"])]),_:1},8,["modelValue","placeholder","prefix-icon"])])]),t("div",ve,[t("section",null,[t("div",{class:"flex items-center gap-2 mb-4 cursor-pointer select-none hover:text-gray-700 dark:hover:text-gray-200 transition-colors",onClick:s[1]||(s[1]=o=>b.value=!b.value)},[l(r,{class:E(["transition-transform duration-200",{"-rotate-90":b.value}])},{default:y(()=>[l(g(z))]),_:1},8,["class"]),l(r,{class:"text-gray-900 dark:text-white",size:20},{default:y(()=>[l(g(W))]),_:1}),t("h3",pe,[R(n(e.$t("tools.systemTools"))+" ",1),t("span",ge,"("+n(w.value.length)+")",1)])]),F(t("div",fe,[(a(!0),i(S,null,V(w.value,o=>(a(),T(L,{key:o.name,title:o.name,description:o.description,"tag-name":e.$t("tools.systemTag"),"tag-class":"bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300",onCall:u=>I(o)},null,8,["title","description","tag-name","onCall"]))),128))],512),[[j,!b.value]])]),t("section",null,[t("div",he,[l(r,{class:"text-gray-900 dark:text-white",size:20},{default:y(()=>[l(g(X))]),_:1}),t("h3",xe,[R(n(e.$t("tools.aggregatedTools"))+" ",1),t("span",ye,"("+n($.value.reduce((o,u)=>o+u.tools.length,0))+")",1)])]),_.value?(a(),i("div",_e,[l(N,{animated:"",count:3})])):$.value.length===0?(a(),i("div",be,n(e.$t("tools.noToolsFound")),1)):(a(),i("div",ke,[(a(!0),i(S,null,V($.value,o=>(a(),i("div",{key:o.serverName,class:"space-y-3"},[t("div",{class:"flex items-center gap-2 text-sm font-medium text-gray-500 dark:text-gray-400 px-1 cursor-pointer select-none hover:text-gray-700 dark:hover:text-gray-200 transition-colors",onClick:u=>q(o.serverName)},[l(r,{class:E(["transition-transform duration-200",{"-rotate-90":h.value.has(o.serverName)}])},{default:y(()=>[l(g(z))]),_:1},8,["class"]),t("span",null,n(o.serverName),1),t("span",Ce,n(o.tools.length),1)],8,we),F(t("div",$e,[(a(!0),i(S,null,V(o.tools,u=>(a(),T(L,{key:u.tool.name,title:u.tool.name,description:u.tool.description,onCall:Te=>I(u.tool)},null,8,["title","description","onCall"]))),128))],512),[[j,!h.value.has(o.serverName)]])]))),128))]))])]),p.value?(a(),T(oe,{key:0,modelValue:C.value,"onUpdate:modelValue":s[2]||(s[2]=o=>C.value=o),"server-name":p.value.serverName,"tool-name":p.value.name,description:p.value.description,"input-schema":p.value.inputSchema},null,8,["modelValue","server-name","tool-name","description","input-schema"])):P("",!0)])}}}),je=ae(Ne,[["__scopeId","data-v-465acdb9"]]);export{je as default};
@@ -1 +0,0 @@
1
- .custom-scrollbar[data-v-465acdb9]::-webkit-scrollbar{width:6px}.custom-scrollbar[data-v-465acdb9]::-webkit-scrollbar-track{background:transparent}.custom-scrollbar[data-v-465acdb9]::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.dark .custom-scrollbar[data-v-465acdb9]::-webkit-scrollbar-thumb{background:#475569}
@@ -1 +0,0 @@
1
- import{b as _,d as B,_ as b,f as h,W as P,X as E,G as I,g as T,Y as $}from"./index-vhkqgpmN.js";import{t as w,A as u,y as i,x as N,U as S,u as t,J as d,d as k,r as V,o as C,w as O,D as j,v,P as m,af as g,B as y,a2 as z,O as L}from"./vue-vendor-BLHLXXJK.js";const R=_({animated:Boolean,count:{type:Number,default:1},rows:{type:Number,default:3},loading:{type:Boolean,default:!0},throttle:{type:B([Number,Object])}}),U=_({variant:{type:String,values:["circle","rect","h1","h3","text","caption","p","image","button"],default:"text"}}),A=w({name:"ElSkeletonItem",__name:"skeleton-item",props:U,setup(l){const e=h("skeleton");return(o,a)=>(i(),u("div",{class:d([t(e).e("item"),t(e).e(o.variant)])},[o.variant==="image"?(i(),N(t(P),{key:0})):S("v-if",!0)],2))}});var p=b(A,[["__file","/home/runner/work/element-plus/element-plus/packages/components/skeleton/src/skeleton-item.vue"]]);const D=(l,e=0)=>{if(e===0)return l;const o=k(e)&&!!e.initVal,a=V(o);let r=null;const s=n=>{if(I(n)){a.value=l.value;return}r&&clearTimeout(r),r=setTimeout(()=>{a.value=l.value},n)},c=n=>{n==="leading"?E(e)?s(e):s(e.leading):k(e)?s(e.trailing):a.value=!1};return C(()=>c("leading")),O(()=>l.value,n=>{c(n?"leading":"trailing")}),a},F=w({name:"ElSkeleton",__name:"skeleton",props:R,setup(l,{expose:e}){const o=l,a=h("skeleton"),r=D(j(o,"loading"),o.throttle);return e({uiLoading:r}),(s,c)=>t(r)?(i(),u("div",y({key:0,class:[t(a).b(),t(a).is("animated",s.animated)]},s.$attrs),[(i(!0),u(m,null,g(s.count,n=>(i(),u(m,{key:n},[t(r)?v(s.$slots,"template",{key:n},()=>[L(p,{class:d(t(a).is("first")),variant:"p"},null,8,["class"]),(i(!0),u(m,null,g(s.rows,f=>(i(),N(p,{key:f,class:d([t(a).e("paragraph"),t(a).is("last",f===s.rows&&s.rows>1)]),variant:"p"},null,8,["class"]))),128))]):S("v-if",!0)],64))),128))],16)):v(s.$slots,"default",z(y({key:1},s.$attrs)))}});var G=b(F,[["__file","/home/runner/work/element-plus/element-plus/packages/components/skeleton/src/skeleton.vue"]]);const M=T(G,{SkeletonItem:p}),W=$(p);export{M as E,W as a};