@revealui/mcp 0.1.0 → 0.1.3
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/LICENSE +22 -0
- package/LICENSE.commercial +111 -0
- package/README.md +3 -0
- package/dist/{packages/mcp/src/adapters → adapters}/db.d.ts +1 -1
- package/dist/adapters/db.d.ts.map +1 -0
- package/dist/adapters/db.js.map +1 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js.map +1 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js.map +1 -0
- package/dist/{packages/mcp/src/hypervisor.d.ts → hypervisor.d.ts} +56 -0
- package/dist/hypervisor.d.ts.map +1 -0
- package/dist/{packages/mcp/src/hypervisor.js → hypervisor.js} +209 -1
- package/dist/hypervisor.js.map +1 -0
- package/dist/{packages/mcp/src/index.d.ts → index.d.ts} +9 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/{packages/mcp/src/index.js → index.js} +8 -4
- package/dist/index.js.map +1 -0
- package/dist/{packages/mcp/src/servers → servers}/adapter.d.ts +11 -1
- package/dist/servers/adapter.d.ts.map +1 -0
- package/dist/{packages/mcp/src/servers → servers}/adapter.js +20 -4
- package/dist/servers/adapter.js.map +1 -0
- package/dist/servers/revealui-content.d.ts +21 -0
- package/dist/servers/revealui-content.d.ts.map +1 -0
- package/dist/servers/revealui-content.js +211 -0
- package/dist/servers/revealui-content.js.map +1 -0
- package/dist/servers/revealui-email.d.ts +18 -0
- package/dist/servers/revealui-email.d.ts.map +1 -0
- package/dist/servers/revealui-email.js +190 -0
- package/dist/servers/revealui-email.js.map +1 -0
- package/dist/servers/revealui-stripe.d.ts +19 -0
- package/dist/servers/revealui-stripe.d.ts.map +1 -0
- package/dist/servers/revealui-stripe.js +228 -0
- package/dist/servers/revealui-stripe.js.map +1 -0
- package/package.json +50 -11
- package/.env.example +0 -9
- package/MCP_MAINTENANCE.md +0 -265
- package/__tests__/crdt.integration.test.ts +0 -156
- package/configs/README.md +0 -77
- package/configs/claude-template.json +0 -54
- package/dist/packages/core/src/database/ssl-config.d.ts +0 -9
- package/dist/packages/core/src/database/ssl-config.d.ts.map +0 -1
- package/dist/packages/core/src/database/ssl-config.js +0 -8
- package/dist/packages/core/src/database/ssl-config.js.map +0 -1
- package/dist/packages/core/src/features.d.ts +0 -86
- package/dist/packages/core/src/features.d.ts.map +0 -1
- package/dist/packages/core/src/features.js +0 -93
- package/dist/packages/core/src/features.js.map +0 -1
- package/dist/packages/core/src/license.d.ts +0 -75
- package/dist/packages/core/src/license.d.ts.map +0 -1
- package/dist/packages/core/src/license.js +0 -174
- package/dist/packages/core/src/license.js.map +0 -1
- package/dist/packages/core/src/monitoring/alerts.d.ts +0 -118
- package/dist/packages/core/src/monitoring/alerts.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/alerts.js +0 -325
- package/dist/packages/core/src/monitoring/alerts.js.map +0 -1
- package/dist/packages/core/src/monitoring/cleanup-manager.d.ts +0 -71
- package/dist/packages/core/src/monitoring/cleanup-manager.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/cleanup-manager.js +0 -227
- package/dist/packages/core/src/monitoring/cleanup-manager.js.map +0 -1
- package/dist/packages/core/src/monitoring/health-monitor.d.ts +0 -22
- package/dist/packages/core/src/monitoring/health-monitor.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/health-monitor.js +0 -143
- package/dist/packages/core/src/monitoring/health-monitor.js.map +0 -1
- package/dist/packages/core/src/monitoring/index.d.ts +0 -14
- package/dist/packages/core/src/monitoring/index.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/index.js +0 -18
- package/dist/packages/core/src/monitoring/index.js.map +0 -1
- package/dist/packages/core/src/monitoring/process-registry.d.ts +0 -97
- package/dist/packages/core/src/monitoring/process-registry.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/process-registry.js +0 -223
- package/dist/packages/core/src/monitoring/process-registry.js.map +0 -1
- package/dist/packages/core/src/monitoring/types.d.ts +0 -231
- package/dist/packages/core/src/monitoring/types.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/types.js +0 -43
- package/dist/packages/core/src/monitoring/types.js.map +0 -1
- package/dist/packages/core/src/monitoring/zombie-detector.d.ts +0 -81
- package/dist/packages/core/src/monitoring/zombie-detector.d.ts.map +0 -1
- package/dist/packages/core/src/monitoring/zombie-detector.js +0 -232
- package/dist/packages/core/src/monitoring/zombie-detector.js.map +0 -1
- package/dist/packages/core/src/observability/logger.d.ts +0 -47
- package/dist/packages/core/src/observability/logger.d.ts.map +0 -1
- package/dist/packages/core/src/observability/logger.js +0 -141
- package/dist/packages/core/src/observability/logger.js.map +0 -1
- package/dist/packages/core/src/utils/logger-server.d.ts +0 -32
- package/dist/packages/core/src/utils/logger-server.d.ts.map +0 -1
- package/dist/packages/core/src/utils/logger-server.js +0 -69
- package/dist/packages/core/src/utils/logger-server.js.map +0 -1
- package/dist/packages/core/src/utils/request-context.d.ts +0 -143
- package/dist/packages/core/src/utils/request-context.d.ts.map +0 -1
- package/dist/packages/core/src/utils/request-context.js +0 -169
- package/dist/packages/core/src/utils/request-context.js.map +0 -1
- package/dist/packages/dev/src/code-validator/index.d.ts +0 -20
- package/dist/packages/dev/src/code-validator/index.d.ts.map +0 -1
- package/dist/packages/dev/src/code-validator/index.js +0 -20
- package/dist/packages/dev/src/code-validator/index.js.map +0 -1
- package/dist/packages/dev/src/code-validator/types.d.ts +0 -67
- package/dist/packages/dev/src/code-validator/types.d.ts.map +0 -1
- package/dist/packages/dev/src/code-validator/types.js +0 -7
- package/dist/packages/dev/src/code-validator/types.js.map +0 -1
- package/dist/packages/dev/src/code-validator/validator.d.ts +0 -48
- package/dist/packages/dev/src/code-validator/validator.d.ts.map +0 -1
- package/dist/packages/dev/src/code-validator/validator.js +0 -176
- package/dist/packages/dev/src/code-validator/validator.js.map +0 -1
- package/dist/packages/mcp/src/adapters/db.d.ts.map +0 -1
- package/dist/packages/mcp/src/adapters/db.js.map +0 -1
- package/dist/packages/mcp/src/config/index.d.ts.map +0 -1
- package/dist/packages/mcp/src/config/index.js.map +0 -1
- package/dist/packages/mcp/src/contracts.d.ts.map +0 -1
- package/dist/packages/mcp/src/contracts.js.map +0 -1
- package/dist/packages/mcp/src/hypervisor.d.ts.map +0 -1
- package/dist/packages/mcp/src/hypervisor.js.map +0 -1
- package/dist/packages/mcp/src/index.d.ts.map +0 -1
- package/dist/packages/mcp/src/index.js.map +0 -1
- package/dist/packages/mcp/src/servers/adapter.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/adapter.js.map +0 -1
- package/dist/packages/mcp/src/servers/code-validator.d.ts +0 -24
- package/dist/packages/mcp/src/servers/code-validator.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/code-validator.js +0 -156
- package/dist/packages/mcp/src/servers/code-validator.js.map +0 -1
- package/dist/packages/mcp/src/servers/neon.d.ts +0 -11
- package/dist/packages/mcp/src/servers/neon.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/neon.js +0 -90
- package/dist/packages/mcp/src/servers/neon.js.map +0 -1
- package/dist/packages/mcp/src/servers/next-devtools.d.ts +0 -11
- package/dist/packages/mcp/src/servers/next-devtools.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/next-devtools.js +0 -215
- package/dist/packages/mcp/src/servers/next-devtools.js.map +0 -1
- package/dist/packages/mcp/src/servers/playwright.d.ts +0 -11
- package/dist/packages/mcp/src/servers/playwright.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/playwright.js +0 -68
- package/dist/packages/mcp/src/servers/playwright.js.map +0 -1
- package/dist/packages/mcp/src/servers/stripe.d.ts +0 -11
- package/dist/packages/mcp/src/servers/stripe.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/stripe.js +0 -86
- package/dist/packages/mcp/src/servers/stripe.js.map +0 -1
- package/dist/packages/mcp/src/servers/supabase.d.ts +0 -11
- package/dist/packages/mcp/src/servers/supabase.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/supabase.js +0 -144
- package/dist/packages/mcp/src/servers/supabase.js.map +0 -1
- package/dist/packages/mcp/src/servers/vercel.d.ts +0 -11
- package/dist/packages/mcp/src/servers/vercel.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/vercel.js +0 -87
- package/dist/packages/mcp/src/servers/vercel.js.map +0 -1
- package/dist/packages/mcp/src/servers/vultr-test.d.ts +0 -3
- package/dist/packages/mcp/src/servers/vultr-test.d.ts.map +0 -1
- package/dist/packages/mcp/src/servers/vultr-test.js +0 -82
- package/dist/packages/mcp/src/servers/vultr-test.js.map +0 -1
- package/dist/scripts/lib/analyzers/console-analyzer.d.ts +0 -188
- package/dist/scripts/lib/analyzers/console-analyzer.d.ts.map +0 -1
- package/dist/scripts/lib/analyzers/console-analyzer.js +0 -432
- package/dist/scripts/lib/analyzers/console-analyzer.js.map +0 -1
- package/dist/scripts/lib/analyzers/index.d.ts +0 -11
- package/dist/scripts/lib/analyzers/index.d.ts.map +0 -1
- package/dist/scripts/lib/analyzers/index.js +0 -11
- package/dist/scripts/lib/analyzers/index.js.map +0 -1
- package/dist/scripts/lib/args.d.ts +0 -104
- package/dist/scripts/lib/args.d.ts.map +0 -1
- package/dist/scripts/lib/args.js +0 -304
- package/dist/scripts/lib/args.js.map +0 -1
- package/dist/scripts/lib/cache.d.ts +0 -185
- package/dist/scripts/lib/cache.d.ts.map +0 -1
- package/dist/scripts/lib/cache.js +0 -390
- package/dist/scripts/lib/cache.js.map +0 -1
- package/dist/scripts/lib/cli/dispatch.d.ts +0 -116
- package/dist/scripts/lib/cli/dispatch.d.ts.map +0 -1
- package/dist/scripts/lib/cli/dispatch.js +0 -206
- package/dist/scripts/lib/cli/dispatch.js.map +0 -1
- package/dist/scripts/lib/cli/index.d.ts +0 -10
- package/dist/scripts/lib/cli/index.d.ts.map +0 -1
- package/dist/scripts/lib/cli/index.js +0 -10
- package/dist/scripts/lib/cli/index.js.map +0 -1
- package/dist/scripts/lib/database/ssl-config.d.ts +0 -26
- package/dist/scripts/lib/database/ssl-config.d.ts.map +0 -1
- package/dist/scripts/lib/database/ssl-config.js +0 -47
- package/dist/scripts/lib/database/ssl-config.js.map +0 -1
- package/dist/scripts/lib/errors.d.ts +0 -218
- package/dist/scripts/lib/errors.d.ts.map +0 -1
- package/dist/scripts/lib/errors.js +0 -543
- package/dist/scripts/lib/errors.js.map +0 -1
- package/dist/scripts/lib/exec.d.ts +0 -107
- package/dist/scripts/lib/exec.d.ts.map +0 -1
- package/dist/scripts/lib/exec.js +0 -232
- package/dist/scripts/lib/exec.js.map +0 -1
- package/dist/scripts/lib/index.d.ts +0 -50
- package/dist/scripts/lib/index.d.ts.map +0 -1
- package/dist/scripts/lib/index.js +0 -65
- package/dist/scripts/lib/index.js.map +0 -1
- package/dist/scripts/lib/logger.d.ts +0 -50
- package/dist/scripts/lib/logger.d.ts.map +0 -1
- package/dist/scripts/lib/logger.js +0 -159
- package/dist/scripts/lib/logger.js.map +0 -1
- package/dist/scripts/lib/output.d.ts +0 -149
- package/dist/scripts/lib/output.d.ts.map +0 -1
- package/dist/scripts/lib/output.js +0 -263
- package/dist/scripts/lib/output.js.map +0 -1
- package/dist/scripts/lib/parallel.d.ts +0 -164
- package/dist/scripts/lib/parallel.d.ts.map +0 -1
- package/dist/scripts/lib/parallel.js +0 -355
- package/dist/scripts/lib/parallel.js.map +0 -1
- package/dist/scripts/lib/paths.d.ts +0 -92
- package/dist/scripts/lib/paths.d.ts.map +0 -1
- package/dist/scripts/lib/paths.js +0 -171
- package/dist/scripts/lib/paths.js.map +0 -1
- package/dist/scripts/lib/state/adapters/memory.d.ts +0 -42
- package/dist/scripts/lib/state/adapters/memory.d.ts.map +0 -1
- package/dist/scripts/lib/state/adapters/memory.js +0 -110
- package/dist/scripts/lib/state/adapters/memory.js.map +0 -1
- package/dist/scripts/lib/state/adapters/pglite.d.ts +0 -46
- package/dist/scripts/lib/state/adapters/pglite.d.ts.map +0 -1
- package/dist/scripts/lib/state/adapters/pglite.js +0 -256
- package/dist/scripts/lib/state/adapters/pglite.js.map +0 -1
- package/dist/scripts/lib/state/index.d.ts +0 -16
- package/dist/scripts/lib/state/index.d.ts.map +0 -1
- package/dist/scripts/lib/state/index.js +0 -16
- package/dist/scripts/lib/state/index.js.map +0 -1
- package/dist/scripts/lib/state/types.d.ts +0 -111
- package/dist/scripts/lib/state/types.d.ts.map +0 -1
- package/dist/scripts/lib/state/types.js +0 -8
- package/dist/scripts/lib/state/types.js.map +0 -1
- package/dist/scripts/lib/state/workflow-state.d.ts +0 -110
- package/dist/scripts/lib/state/workflow-state.d.ts.map +0 -1
- package/dist/scripts/lib/state/workflow-state.js +0 -331
- package/dist/scripts/lib/state/workflow-state.js.map +0 -1
- package/dist/scripts/lib/telemetry.d.ts +0 -194
- package/dist/scripts/lib/telemetry.d.ts.map +0 -1
- package/dist/scripts/lib/telemetry.js +0 -394
- package/dist/scripts/lib/telemetry.js.map +0 -1
- package/dist/scripts/lib/utils.d.ts +0 -270
- package/dist/scripts/lib/utils.d.ts.map +0 -1
- package/dist/scripts/lib/utils.js +0 -473
- package/dist/scripts/lib/utils.js.map +0 -1
- package/dist/scripts/lib/validation/database.d.ts +0 -83
- package/dist/scripts/lib/validation/database.d.ts.map +0 -1
- package/dist/scripts/lib/validation/database.js +0 -199
- package/dist/scripts/lib/validation/database.js.map +0 -1
- package/dist/scripts/lib/validation/env.d.ts +0 -80
- package/dist/scripts/lib/validation/env.d.ts.map +0 -1
- package/dist/scripts/lib/validation/env.js +0 -246
- package/dist/scripts/lib/validation/env.js.map +0 -1
- package/dist/scripts/lib/validation/index.d.ts +0 -16
- package/dist/scripts/lib/validation/index.d.ts.map +0 -1
- package/dist/scripts/lib/validation/index.js +0 -16
- package/dist/scripts/lib/validation/index.js.map +0 -1
- package/dist/scripts/lib/validation/post-execution.d.ts +0 -74
- package/dist/scripts/lib/validation/post-execution.d.ts.map +0 -1
- package/dist/scripts/lib/validation/post-execution.js +0 -110
- package/dist/scripts/lib/validation/post-execution.js.map +0 -1
- package/dist/scripts/lib/validation/pre-execution.d.ts +0 -165
- package/dist/scripts/lib/validation/pre-execution.d.ts.map +0 -1
- package/dist/scripts/lib/validation/pre-execution.js +0 -466
- package/dist/scripts/lib/validation/pre-execution.js.map +0 -1
- package/dist/scripts/lib/validators/documentation-validator.d.ts +0 -242
- package/dist/scripts/lib/validators/documentation-validator.d.ts.map +0 -1
- package/dist/scripts/lib/validators/documentation-validator.js +0 -584
- package/dist/scripts/lib/validators/documentation-validator.js.map +0 -1
- package/dist/scripts/lib/validators/index.d.ts +0 -11
- package/dist/scripts/lib/validators/index.d.ts.map +0 -1
- package/dist/scripts/lib/validators/index.js +0 -11
- package/dist/scripts/lib/validators/index.js.map +0 -1
- package/docker-compose.yml +0 -46
- package/docs/INDEX.md +0 -88
- package/docs/README.md +0 -774
- package/docs/SETUP.md +0 -264
- package/docs/servers/code-validator.md +0 -586
- package/eslint.config.js +0 -7
- package/migrations/0001_add_crdt_columns.sql +0 -8
- package/migrations/0001_rollback.sql +0 -6
- package/migrations/005_performance_indexes.sql +0 -190
- package/migrations/backfill_crdt_meta.js +0 -45
- package/src/__tests__/hypervisor.test.ts +0 -212
- package/src/adapters/db.ts +0 -180
- package/src/config/config.json +0 -49
- package/src/config/index.ts +0 -30
- package/src/contracts.ts +0 -221
- package/src/hypervisor.ts +0 -464
- package/src/index.ts +0 -87
- package/src/servers/adapter.ts +0 -643
- package/src/servers/code-validator.ts +0 -188
- package/src/servers/neon.ts +0 -103
- package/src/servers/next-devtools.ts +0 -230
- package/src/servers/playwright.ts +0 -77
- package/src/servers/stripe.ts +0 -99
- package/src/servers/supabase.ts +0 -161
- package/src/servers/vercel.ts +0 -100
- package/src/servers/vultr-test.ts +0 -97
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -22
- /package/dist/{packages/mcp/src/adapters → adapters}/db.js +0 -0
- /package/dist/{packages/mcp/src/config → config}/index.d.ts +0 -0
- /package/dist/{packages/mcp/src/config → config}/index.js +0 -0
- /package/dist/{packages/mcp/src/contracts.d.ts → contracts.d.ts} +0 -0
- /package/dist/{packages/mcp/src/contracts.js → contracts.js} +0 -0
package/src/config/index.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export type McpMetricsMode = 'logs' | 'otel' | 'prometheus'
|
|
2
|
-
|
|
3
|
-
export interface McpConfig {
|
|
4
|
-
persistenceDriver: 'pglite' | 'postgres'
|
|
5
|
-
electricDatabaseUrl: string | null
|
|
6
|
-
electricApiKey: string | null
|
|
7
|
-
metricsMode: McpMetricsMode
|
|
8
|
-
pgvectorEnabled: boolean
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function boolFromEnv(v: string | undefined, fallback = false) {
|
|
12
|
-
if (v === undefined) return fallback
|
|
13
|
-
return ['1', 'true', 'yes'].includes(v.toLowerCase())
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function getMcpConfig(): McpConfig {
|
|
17
|
-
const persistenceDriver =
|
|
18
|
-
(process.env.MCP_PERSISTENCE_DRIVER as 'pglite' | 'postgres') || 'pglite'
|
|
19
|
-
const metricsMode = (process.env.MCP_METRICS_MODE as McpMetricsMode) || 'logs'
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
persistenceDriver,
|
|
23
|
-
electricDatabaseUrl: process.env.ELECTRIC_DATABASE_URL || null,
|
|
24
|
-
electricApiKey: process.env.ELECTRIC_API_KEY || null,
|
|
25
|
-
metricsMode,
|
|
26
|
-
pgvectorEnabled: boolFromEnv(process.env.PGVECTOR_ENABLED, false),
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default getMcpConfig
|
package/src/contracts.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Contracts
|
|
3
|
-
*
|
|
4
|
-
* Zod schemas for MCP-specific types, built on top of @revealui/contracts.
|
|
5
|
-
* These provide runtime validation for MCP request/response payloads
|
|
6
|
-
* and bridge MCP SDK tool definitions to contracts ToolDefinition.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
type A2AAgentCard,
|
|
11
|
-
type A2AAuth,
|
|
12
|
-
type AgentDefinition,
|
|
13
|
-
agentDefinitionToCard,
|
|
14
|
-
type ToolDefinition,
|
|
15
|
-
ToolDefinitionSchema,
|
|
16
|
-
type ToolParameter,
|
|
17
|
-
z,
|
|
18
|
-
} from '@revealui/contracts'
|
|
19
|
-
|
|
20
|
-
// =============================================================================
|
|
21
|
-
// MCP Request / Response Schemas
|
|
22
|
-
// =============================================================================
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Schema for MCP request options (idempotency, retry, dry-run)
|
|
26
|
-
*/
|
|
27
|
-
export const MCPRequestOptionsSchema = z.object({
|
|
28
|
-
timeout: z.number().positive().optional(),
|
|
29
|
-
retries: z.number().int().nonnegative().optional(),
|
|
30
|
-
dryRun: z.boolean().optional(),
|
|
31
|
-
idempotencyKey: z.string().optional(),
|
|
32
|
-
idempotencyTTL: z.number().positive().optional(),
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
export type MCPRequestOptions = z.infer<typeof MCPRequestOptionsSchema>
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Schema for an MCP request payload
|
|
39
|
-
*/
|
|
40
|
-
export const MCPRequestSchema = z.object({
|
|
41
|
-
action: z.string().min(1),
|
|
42
|
-
parameters: z.record(z.string(), z.unknown()).optional(),
|
|
43
|
-
options: MCPRequestOptionsSchema.optional(),
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
export type MCPRequest = z.infer<typeof MCPRequestSchema>
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Schema for MCP response metadata
|
|
50
|
-
*/
|
|
51
|
-
export const MCPResponseMetadataSchema = z.object({
|
|
52
|
-
duration: z.number().nonnegative(),
|
|
53
|
-
retries: z.number().int().nonnegative(),
|
|
54
|
-
service: z.string(),
|
|
55
|
-
cached: z.boolean().optional(),
|
|
56
|
-
idempotencyKey: z.string().optional(),
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
export type MCPResponseMetadata = z.infer<typeof MCPResponseMetadataSchema>
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Schema for an MCP response payload
|
|
63
|
-
*/
|
|
64
|
-
export const MCPResponseSchema = z.object({
|
|
65
|
-
success: z.boolean(),
|
|
66
|
-
data: z.unknown().optional(),
|
|
67
|
-
error: z.string().optional(),
|
|
68
|
-
metadata: MCPResponseMetadataSchema.optional(),
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
export type MCPResponse = z.infer<typeof MCPResponseSchema>
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Schema for MCP adapter configuration
|
|
75
|
-
*/
|
|
76
|
-
export const MCPAdapterConfigSchema = z.object({
|
|
77
|
-
apiKey: z.string().optional(),
|
|
78
|
-
baseUrl: z.string().url().optional(),
|
|
79
|
-
timeout: z.number().positive().optional(),
|
|
80
|
-
retries: z.number().int().nonnegative().optional(),
|
|
81
|
-
environment: z.enum(['development', 'production']).optional(),
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
export type MCPAdapterConfig = z.infer<typeof MCPAdapterConfigSchema>
|
|
85
|
-
|
|
86
|
-
// =============================================================================
|
|
87
|
-
// Tool Definition Bridge
|
|
88
|
-
// =============================================================================
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Converts an MCP SDK tool definition (JSON Schema-based) to a contracts ToolDefinition.
|
|
92
|
-
*
|
|
93
|
-
* The MCP SDK uses JSON Schema for tool `inputSchema`, while contracts uses
|
|
94
|
-
* a structured ToolParameter format. This bridge maps between them.
|
|
95
|
-
*/
|
|
96
|
-
export function mcpToolToContractsToolDefinition(mcpTool: {
|
|
97
|
-
name: string
|
|
98
|
-
description?: string
|
|
99
|
-
inputSchema?: {
|
|
100
|
-
type: string
|
|
101
|
-
properties?: Record<
|
|
102
|
-
string,
|
|
103
|
-
{ type?: string; description?: string; default?: unknown; enum?: string[] }
|
|
104
|
-
>
|
|
105
|
-
required?: string[]
|
|
106
|
-
}
|
|
107
|
-
}): ToolDefinition {
|
|
108
|
-
const parameters: Record<string, ToolParameter> = {}
|
|
109
|
-
|
|
110
|
-
if (mcpTool.inputSchema?.properties) {
|
|
111
|
-
const required = new Set(mcpTool.inputSchema.required ?? [])
|
|
112
|
-
|
|
113
|
-
for (const [key, prop] of Object.entries(mcpTool.inputSchema.properties)) {
|
|
114
|
-
parameters[key] = {
|
|
115
|
-
type: (prop.type as ToolParameter['type']) ?? 'string',
|
|
116
|
-
description: prop.description ?? '',
|
|
117
|
-
required: required.has(key),
|
|
118
|
-
...(prop.default !== undefined && { default: prop.default }),
|
|
119
|
-
...(prop.enum && { enum: prop.enum }),
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return ToolDefinitionSchema.parse({
|
|
125
|
-
name: mcpTool.name,
|
|
126
|
-
description: mcpTool.description ?? `MCP tool: ${mcpTool.name}`,
|
|
127
|
-
parameters,
|
|
128
|
-
destructive: false,
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Converts a contracts ToolDefinition back to an MCP SDK-compatible tool shape.
|
|
134
|
-
* Useful for registering contracts-defined tools with MCP servers.
|
|
135
|
-
*/
|
|
136
|
-
export function contractsToolDefinitionToMcpTool(tool: ToolDefinition): {
|
|
137
|
-
name: string
|
|
138
|
-
description: string
|
|
139
|
-
inputSchema: {
|
|
140
|
-
type: 'object'
|
|
141
|
-
properties: Record<
|
|
142
|
-
string,
|
|
143
|
-
{ type: string; description: string; default?: unknown; enum?: string[] }
|
|
144
|
-
>
|
|
145
|
-
required: string[]
|
|
146
|
-
}
|
|
147
|
-
} {
|
|
148
|
-
const properties: Record<
|
|
149
|
-
string,
|
|
150
|
-
{ type: string; description: string; default?: unknown; enum?: string[] }
|
|
151
|
-
> = {}
|
|
152
|
-
const required: string[] = []
|
|
153
|
-
|
|
154
|
-
for (const [key, param] of Object.entries(tool.parameters)) {
|
|
155
|
-
properties[key] = {
|
|
156
|
-
type: param.type,
|
|
157
|
-
description: param.description,
|
|
158
|
-
...(param.default !== undefined && { default: param.default }),
|
|
159
|
-
...(param.enum && { enum: param.enum }),
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (param.required) {
|
|
163
|
-
required.push(key)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
name: tool.name,
|
|
169
|
-
description: tool.description,
|
|
170
|
-
inputSchema: {
|
|
171
|
-
type: 'object',
|
|
172
|
-
properties,
|
|
173
|
-
required,
|
|
174
|
-
},
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// =============================================================================
|
|
179
|
-
// Agent Card Bridge
|
|
180
|
-
// =============================================================================
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Converts a RevealUI AgentDefinition to a Google A2A AgentCard.
|
|
184
|
-
* Wraps the contracts-level `agentDefinitionToCard` with optional MCP-specific overrides.
|
|
185
|
-
*
|
|
186
|
-
* @param agent - The agent definition (source of truth)
|
|
187
|
-
* @param baseUrl - The server base URL (e.g. https://api.revealui.com)
|
|
188
|
-
* @param opts - Optional overrides for auth scheme and streaming capability
|
|
189
|
-
*/
|
|
190
|
-
export function agentDefinitionToAgentCard(
|
|
191
|
-
agent: AgentDefinition,
|
|
192
|
-
baseUrl: string,
|
|
193
|
-
opts?: { authScheme?: A2AAuth; streaming?: boolean },
|
|
194
|
-
): A2AAgentCard {
|
|
195
|
-
const card = agentDefinitionToCard(agent, baseUrl)
|
|
196
|
-
if (!opts) return card
|
|
197
|
-
|
|
198
|
-
const { authScheme, streaming } = opts
|
|
199
|
-
return {
|
|
200
|
-
...card,
|
|
201
|
-
...(authScheme !== undefined && { authentication: authScheme }),
|
|
202
|
-
capabilities: {
|
|
203
|
-
...(card.capabilities ?? {
|
|
204
|
-
streaming: false,
|
|
205
|
-
pushNotifications: false,
|
|
206
|
-
stateTransitionHistory: false,
|
|
207
|
-
}),
|
|
208
|
-
streaming: streaming ?? card.capabilities?.streaming ?? false,
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Converts all tools in a RevealUI AgentDefinition to MCP tool specs.
|
|
215
|
-
* Uses `contractsToolDefinitionToMcpTool` for each tool.
|
|
216
|
-
*/
|
|
217
|
-
export function agentDefinitionToMcpTools(
|
|
218
|
-
agent: AgentDefinition,
|
|
219
|
-
): ReturnType<typeof contractsToolDefinitionToMcpTool>[] {
|
|
220
|
-
return agent.tools.map(contractsToolDefinitionToMcpTool)
|
|
221
|
-
}
|
package/src/hypervisor.ts
DELETED
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Hypervisor
|
|
3
|
-
*
|
|
4
|
-
* Manages N running MCP server processes, pings them for liveness, and
|
|
5
|
-
* dynamically exposes their tools at runtime. Inspired by the
|
|
6
|
-
* MCPCompatibilityLayer/MCPHypervisor pattern from AnythingLLM.
|
|
7
|
-
*
|
|
8
|
-
* Architecture:
|
|
9
|
-
* - Singleton: one hypervisor per process manages all MCP servers
|
|
10
|
-
* - Each server is spawned with piped stdio for JSON-RPC communication
|
|
11
|
-
* - Tool names are namespaced: @@mcp_{serverName}_{toolName}
|
|
12
|
-
* - Health check loop: every 60s, process.exitCode check + tools/list probe
|
|
13
|
-
*
|
|
14
|
-
* Wire format: newline-delimited JSON-RPC 2.0 (stdin/stdout)
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { type ChildProcess, spawn } from 'node:child_process'
|
|
18
|
-
import { registerCleanupHandler } from '@revealui/core/monitoring'
|
|
19
|
-
import { logger } from '@revealui/core/observability/logger'
|
|
20
|
-
|
|
21
|
-
// =============================================================================
|
|
22
|
-
// Types
|
|
23
|
-
// =============================================================================
|
|
24
|
-
|
|
25
|
-
export interface MCPServerConfig {
|
|
26
|
-
/** Unique name for this server (used in tool namespacing) */
|
|
27
|
-
name: string
|
|
28
|
-
/** Executable to run (e.g. 'node', 'pnpm') */
|
|
29
|
-
command: string
|
|
30
|
-
/** Arguments to the command */
|
|
31
|
-
args: string[]
|
|
32
|
-
/** Additional environment variables */
|
|
33
|
-
env?: Record<string, string>
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface MCPTool {
|
|
37
|
-
name: string
|
|
38
|
-
description: string
|
|
39
|
-
inputSchema: {
|
|
40
|
-
type: 'object'
|
|
41
|
-
properties?: Record<string, unknown>
|
|
42
|
-
required?: string[]
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface NamespacedTool {
|
|
47
|
-
/** Namespaced name: @@mcp_{serverName}_{toolName} */
|
|
48
|
-
namespacedName: string
|
|
49
|
-
serverName: string
|
|
50
|
-
tool: MCPTool
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface ServerEntry {
|
|
54
|
-
config: MCPServerConfig
|
|
55
|
-
process: ChildProcess | null
|
|
56
|
-
tools: MCPTool[]
|
|
57
|
-
healthy: boolean
|
|
58
|
-
lastPingAt: number | null
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
interface JsonRpcRequest {
|
|
62
|
-
jsonrpc: '2.0'
|
|
63
|
-
id: number
|
|
64
|
-
method: string
|
|
65
|
-
params?: unknown
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
interface JsonRpcResponse {
|
|
69
|
-
jsonrpc: '2.0'
|
|
70
|
-
id: number
|
|
71
|
-
result?: unknown
|
|
72
|
-
error?: { code: number; message: string }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// =============================================================================
|
|
76
|
-
// Constants
|
|
77
|
-
// =============================================================================
|
|
78
|
-
|
|
79
|
-
const HEALTH_CHECK_INTERVAL_MS = 60_000
|
|
80
|
-
const REQUEST_TIMEOUT_MS = 5_000
|
|
81
|
-
const MCP_TOOL_PREFIX = '@@mcp'
|
|
82
|
-
|
|
83
|
-
// =============================================================================
|
|
84
|
-
// MCPHypervisor
|
|
85
|
-
// =============================================================================
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Singleton that manages MCP server processes and their tool registries.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* const hypervisor = MCPHypervisor.getInstance()
|
|
93
|
-
*
|
|
94
|
-
* hypervisor.registerServer({
|
|
95
|
-
* name: 'stripe',
|
|
96
|
-
* command: 'pnpm',
|
|
97
|
-
* args: ['dlx', '@stripe/mcp', '--tools=all', '--api-key=sk_...'],
|
|
98
|
-
* })
|
|
99
|
-
*
|
|
100
|
-
* await hypervisor.startServer('stripe')
|
|
101
|
-
* const tools = hypervisor.getAllTools()
|
|
102
|
-
* // tools[0].namespacedName === '@@mcp_stripe_create_payment_intent'
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
export class MCPHypervisor {
|
|
106
|
-
private static instance: MCPHypervisor | null = null
|
|
107
|
-
|
|
108
|
-
private servers: Map<string, ServerEntry> = new Map()
|
|
109
|
-
private requestCounter = 0
|
|
110
|
-
private pendingRequests: Map<
|
|
111
|
-
number,
|
|
112
|
-
{ resolve: (value: unknown) => void; reject: (error: Error) => void; timer: NodeJS.Timeout }
|
|
113
|
-
> = new Map()
|
|
114
|
-
private healthCheckTimer: NodeJS.Timeout | null = null
|
|
115
|
-
|
|
116
|
-
private constructor() {
|
|
117
|
-
registerCleanupHandler(
|
|
118
|
-
'mcp-hypervisor',
|
|
119
|
-
async () => this.stopAll(),
|
|
120
|
-
'Stop all MCP server processes',
|
|
121
|
-
85,
|
|
122
|
-
)
|
|
123
|
-
this.startHealthCheckLoop()
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
static getInstance(): MCPHypervisor {
|
|
127
|
-
if (!MCPHypervisor.instance) {
|
|
128
|
-
MCPHypervisor.instance = new MCPHypervisor()
|
|
129
|
-
}
|
|
130
|
-
return MCPHypervisor.instance
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ---------------------------------------------------------------------------
|
|
134
|
-
// Server registration
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Register an MCP server configuration without starting it.
|
|
139
|
-
*/
|
|
140
|
-
registerServer(config: MCPServerConfig): void {
|
|
141
|
-
if (this.servers.has(config.name)) {
|
|
142
|
-
logger.warn(`[MCPHypervisor] Server "${config.name}" is already registered`)
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
this.servers.set(config.name, {
|
|
146
|
-
config,
|
|
147
|
-
process: null,
|
|
148
|
-
tools: [],
|
|
149
|
-
healthy: false,
|
|
150
|
-
lastPingAt: null,
|
|
151
|
-
})
|
|
152
|
-
logger.info(`[MCPHypervisor] Registered server: ${config.name}`)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Unregister a server (stops it first if running).
|
|
157
|
-
*/
|
|
158
|
-
async unregisterServer(name: string): Promise<void> {
|
|
159
|
-
const entry = this.servers.get(name)
|
|
160
|
-
if (!entry) return
|
|
161
|
-
if (entry.process) await this.stopServer(name)
|
|
162
|
-
this.servers.delete(name)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ---------------------------------------------------------------------------
|
|
166
|
-
// Lifecycle
|
|
167
|
-
// ---------------------------------------------------------------------------
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Spawn the MCP server process with piped stdio.
|
|
171
|
-
* Tools are discovered via `listServerTools()` after startup.
|
|
172
|
-
*/
|
|
173
|
-
async startServer(name: string): Promise<void> {
|
|
174
|
-
const entry = this.servers.get(name)
|
|
175
|
-
if (!entry) throw new Error(`[MCPHypervisor] Unknown server: "${name}"`)
|
|
176
|
-
if (entry.process && entry.process.exitCode === null) {
|
|
177
|
-
logger.info(`[MCPHypervisor] Server "${name}" is already running`)
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const { config } = entry
|
|
182
|
-
const child = spawn(config.command, config.args, {
|
|
183
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
184
|
-
env: { ...process.env, ...config.env },
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
entry.process = child
|
|
188
|
-
entry.healthy = false
|
|
189
|
-
entry.tools = []
|
|
190
|
-
|
|
191
|
-
// Buffer incoming stdout for JSON-RPC response parsing
|
|
192
|
-
let buffer = ''
|
|
193
|
-
child.stdout?.on('data', (chunk: Buffer) => {
|
|
194
|
-
buffer += chunk.toString()
|
|
195
|
-
const lines = buffer.split('\n')
|
|
196
|
-
buffer = lines.pop() ?? '' // keep incomplete line in buffer
|
|
197
|
-
for (const line of lines) {
|
|
198
|
-
const trimmed = line.trim()
|
|
199
|
-
if (!trimmed) continue
|
|
200
|
-
try {
|
|
201
|
-
const msg = JSON.parse(trimmed) as JsonRpcResponse
|
|
202
|
-
this.handleResponse(msg)
|
|
203
|
-
} catch {
|
|
204
|
-
// Non-JSON output from server — ignore
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
child.stderr?.on('data', (chunk: Buffer) => {
|
|
210
|
-
logger.warn(`[MCPHypervisor] ${name} stderr: ${chunk.toString().trim()}`)
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
child.on('exit', (code) => {
|
|
214
|
-
logger.warn(`[MCPHypervisor] Server "${name}" exited with code ${code}`)
|
|
215
|
-
entry.healthy = false
|
|
216
|
-
// Reject all pending requests for this server
|
|
217
|
-
for (const [id, pending] of this.pendingRequests) {
|
|
218
|
-
pending.reject(new Error(`Server "${name}" exited`))
|
|
219
|
-
clearTimeout(pending.timer)
|
|
220
|
-
this.pendingRequests.delete(id)
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// Allow a brief startup window, then probe tools
|
|
225
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 500))
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
await this.listServerTools(name)
|
|
229
|
-
entry.healthy = true
|
|
230
|
-
logger.info(`[MCPHypervisor] Server "${name}" started (${entry.tools.length} tools)`)
|
|
231
|
-
} catch (error) {
|
|
232
|
-
logger.warn(
|
|
233
|
-
`[MCPHypervisor] Server "${name}" started but tool discovery failed: ${
|
|
234
|
-
error instanceof Error ? error.message : String(error)
|
|
235
|
-
}`,
|
|
236
|
-
)
|
|
237
|
-
// Still mark healthy if the process is alive (tools may not be supported)
|
|
238
|
-
entry.healthy = entry.process.exitCode === null
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Stop a running MCP server process.
|
|
244
|
-
*/
|
|
245
|
-
async stopServer(name: string): Promise<void> {
|
|
246
|
-
const entry = this.servers.get(name)
|
|
247
|
-
if (!entry?.process) return
|
|
248
|
-
|
|
249
|
-
entry.process.kill('SIGTERM')
|
|
250
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 200))
|
|
251
|
-
|
|
252
|
-
if (entry.process.exitCode === null) {
|
|
253
|
-
entry.process.kill('SIGKILL')
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
entry.process = null
|
|
257
|
-
entry.healthy = false
|
|
258
|
-
entry.tools = []
|
|
259
|
-
logger.info(`[MCPHypervisor] Stopped server: ${name}`)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Stop all running servers.
|
|
264
|
-
*/
|
|
265
|
-
async stopAll(): Promise<void> {
|
|
266
|
-
this.stopHealthCheckLoop()
|
|
267
|
-
await Promise.all(Array.from(this.servers.keys()).map((name) => this.stopServer(name)))
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// ---------------------------------------------------------------------------
|
|
271
|
-
// JSON-RPC communication
|
|
272
|
-
// ---------------------------------------------------------------------------
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Send a JSON-RPC request to a running server and await the response.
|
|
276
|
-
*/
|
|
277
|
-
private sendRequest(name: string, method: string, params?: unknown): Promise<unknown> {
|
|
278
|
-
const entry = this.servers.get(name)
|
|
279
|
-
if (!entry?.process || entry.process.exitCode !== null) {
|
|
280
|
-
return Promise.reject(new Error(`Server "${name}" is not running`))
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const id = ++this.requestCounter
|
|
284
|
-
const request: JsonRpcRequest = {
|
|
285
|
-
jsonrpc: '2.0',
|
|
286
|
-
id,
|
|
287
|
-
method,
|
|
288
|
-
...(params !== undefined && { params }),
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return new Promise((resolve, reject) => {
|
|
292
|
-
const timer = setTimeout(() => {
|
|
293
|
-
this.pendingRequests.delete(id)
|
|
294
|
-
reject(new Error(`Request ${method} timed out after ${REQUEST_TIMEOUT_MS}ms`))
|
|
295
|
-
}, REQUEST_TIMEOUT_MS)
|
|
296
|
-
|
|
297
|
-
this.pendingRequests.set(id, { resolve, reject, timer })
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
entry.process?.stdin?.write(`${JSON.stringify(request)}\n`)
|
|
301
|
-
} catch (error) {
|
|
302
|
-
clearTimeout(timer)
|
|
303
|
-
this.pendingRequests.delete(id)
|
|
304
|
-
reject(error)
|
|
305
|
-
}
|
|
306
|
-
})
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private handleResponse(msg: JsonRpcResponse): void {
|
|
310
|
-
const pending = this.pendingRequests.get(msg.id)
|
|
311
|
-
if (!pending) return
|
|
312
|
-
|
|
313
|
-
clearTimeout(pending.timer)
|
|
314
|
-
this.pendingRequests.delete(msg.id)
|
|
315
|
-
|
|
316
|
-
if (msg.error) {
|
|
317
|
-
pending.reject(new Error(`JSON-RPC error ${msg.error.code}: ${msg.error.message}`))
|
|
318
|
-
} else {
|
|
319
|
-
pending.resolve(msg.result)
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// ---------------------------------------------------------------------------
|
|
324
|
-
// Health checks
|
|
325
|
-
// ---------------------------------------------------------------------------
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Ping a server — checks process liveness and sends a JSON-RPC `ping`.
|
|
329
|
-
* Updates `entry.healthy`.
|
|
330
|
-
*/
|
|
331
|
-
async pingServer(name: string): Promise<boolean> {
|
|
332
|
-
const entry = this.servers.get(name)
|
|
333
|
-
if (!entry) return false
|
|
334
|
-
|
|
335
|
-
// Process liveness check
|
|
336
|
-
if (!entry.process || entry.process.exitCode !== null) {
|
|
337
|
-
entry.healthy = false
|
|
338
|
-
return false
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
entry.lastPingAt = Date.now()
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
await this.sendRequest(name, 'ping')
|
|
345
|
-
entry.healthy = true
|
|
346
|
-
return true
|
|
347
|
-
} catch {
|
|
348
|
-
// ping not supported by all servers — still healthy if process is alive
|
|
349
|
-
entry.healthy = entry.process.exitCode === null
|
|
350
|
-
return entry.healthy
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// ---------------------------------------------------------------------------
|
|
355
|
-
// Tool discovery
|
|
356
|
-
// ---------------------------------------------------------------------------
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Discover tools from a running server via JSON-RPC `tools/list`.
|
|
360
|
-
* Caches the result in the server entry.
|
|
361
|
-
*/
|
|
362
|
-
async listServerTools(name: string): Promise<MCPTool[]> {
|
|
363
|
-
const entry = this.servers.get(name)
|
|
364
|
-
if (!entry) throw new Error(`Unknown server: "${name}"`)
|
|
365
|
-
|
|
366
|
-
const result = await this.sendRequest(name, 'tools/list')
|
|
367
|
-
|
|
368
|
-
const tools = (result as { tools?: MCPTool[] })?.tools ?? []
|
|
369
|
-
entry.tools = tools
|
|
370
|
-
return tools
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Return all tools from all healthy servers, namespaced as
|
|
375
|
-
* `@@mcp_{serverName}_{toolName}` to avoid collisions.
|
|
376
|
-
*/
|
|
377
|
-
getAllTools(): NamespacedTool[] {
|
|
378
|
-
const tools: NamespacedTool[] = []
|
|
379
|
-
|
|
380
|
-
for (const [serverName, entry] of this.servers) {
|
|
381
|
-
if (!entry.healthy) continue
|
|
382
|
-
|
|
383
|
-
for (const tool of entry.tools) {
|
|
384
|
-
tools.push({
|
|
385
|
-
namespacedName: `${MCP_TOOL_PREFIX}_${serverName}_${tool.name}`,
|
|
386
|
-
serverName,
|
|
387
|
-
tool,
|
|
388
|
-
})
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return tools
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Call a tool on a running MCP server via JSON-RPC `tools/call`.
|
|
397
|
-
*
|
|
398
|
-
* @param serverName - The registered server name
|
|
399
|
-
* @param toolName - The tool name (without namespace prefix)
|
|
400
|
-
* @param args - Arguments to pass to the tool
|
|
401
|
-
*/
|
|
402
|
-
async callTool(serverName: string, toolName: string, args: unknown): Promise<unknown> {
|
|
403
|
-
return this.sendRequest(serverName, 'tools/call', {
|
|
404
|
-
name: toolName,
|
|
405
|
-
arguments: args,
|
|
406
|
-
})
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Return the health status of all registered servers.
|
|
411
|
-
*/
|
|
412
|
-
getStatus(): Record<string, { healthy: boolean; toolCount: number; pid: number | null }> {
|
|
413
|
-
const status: Record<string, { healthy: boolean; toolCount: number; pid: number | null }> = {}
|
|
414
|
-
for (const [name, entry] of this.servers) {
|
|
415
|
-
status[name] = {
|
|
416
|
-
healthy: entry.healthy,
|
|
417
|
-
toolCount: entry.tools.length,
|
|
418
|
-
pid: entry.process?.pid ?? null,
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return status
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// ---------------------------------------------------------------------------
|
|
425
|
-
// Health check loop
|
|
426
|
-
// ---------------------------------------------------------------------------
|
|
427
|
-
|
|
428
|
-
private startHealthCheckLoop(): void {
|
|
429
|
-
this.healthCheckTimer = setInterval(async () => {
|
|
430
|
-
for (const [name] of this.servers) {
|
|
431
|
-
try {
|
|
432
|
-
await this.pingServer(name)
|
|
433
|
-
} catch {
|
|
434
|
-
// Already handled inside pingServer
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}, HEALTH_CHECK_INTERVAL_MS)
|
|
438
|
-
|
|
439
|
-
// Don't prevent process exit
|
|
440
|
-
this.healthCheckTimer.unref?.()
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
private stopHealthCheckLoop(): void {
|
|
444
|
-
if (this.healthCheckTimer) {
|
|
445
|
-
clearInterval(this.healthCheckTimer)
|
|
446
|
-
this.healthCheckTimer = null
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// ---------------------------------------------------------------------------
|
|
451
|
-
// Testing utilities
|
|
452
|
-
// ---------------------------------------------------------------------------
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Reset the singleton (for testing only).
|
|
456
|
-
* @internal
|
|
457
|
-
*/
|
|
458
|
-
static _resetForTests(): void {
|
|
459
|
-
if (MCPHypervisor.instance) {
|
|
460
|
-
MCPHypervisor.instance.stopHealthCheckLoop()
|
|
461
|
-
MCPHypervisor.instance = null
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|