@decocms/mesh-sdk 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -0
- package/package.json +51 -0
- package/src/context/index.ts +9 -0
- package/src/context/project-context.tsx +89 -0
- package/src/hooks/index.ts +73 -0
- package/src/hooks/use-collections.ts +357 -0
- package/src/hooks/use-connection.ts +82 -0
- package/src/hooks/use-mcp-client.ts +127 -0
- package/src/hooks/use-mcp-prompts.ts +126 -0
- package/src/hooks/use-mcp-resources.ts +128 -0
- package/src/hooks/use-mcp-tools.ts +184 -0
- package/src/hooks/use-virtual-mcp.ts +91 -0
- package/src/index.ts +128 -0
- package/src/lib/constants.ts +204 -0
- package/src/lib/mcp-oauth.ts +742 -0
- package/src/lib/query-keys.ts +178 -0
- package/src/lib/streamable-http-client-transport.ts +79 -0
- package/src/types/connection.ts +204 -0
- package/src/types/index.ts +27 -0
- package/src/types/virtual-mcp.ts +218 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized Query Keys for React Query
|
|
3
|
+
*
|
|
4
|
+
* This ensures consistent cache key management across the application
|
|
5
|
+
* and prevents inline array declarations that are harder to maintain.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ProjectLocator } from "../context";
|
|
9
|
+
|
|
10
|
+
export const KEYS = {
|
|
11
|
+
// Auth-related queries
|
|
12
|
+
authConfig: () => ["authConfig"] as const,
|
|
13
|
+
|
|
14
|
+
// Chat store (IndexedDB) queries
|
|
15
|
+
threads: (locator: string) => ["threads", locator] as const,
|
|
16
|
+
virtualMcpThreads: (locator: string, virtualMcpId: string) =>
|
|
17
|
+
["threads", locator, "virtual-mcp", virtualMcpId] as const,
|
|
18
|
+
thread: (locator: string, threadId: string) =>
|
|
19
|
+
["thread", locator, threadId] as const,
|
|
20
|
+
threadMessages: (locator: string, threadId: string) =>
|
|
21
|
+
["thread-messages", locator, threadId] as const,
|
|
22
|
+
messages: (locator: string) => ["messages", locator] as const,
|
|
23
|
+
|
|
24
|
+
// Organizations list
|
|
25
|
+
organizations: () => ["organizations"] as const,
|
|
26
|
+
|
|
27
|
+
// Organization members (scoped by org)
|
|
28
|
+
members: (locator: ProjectLocator) => [locator, "members"] as const,
|
|
29
|
+
|
|
30
|
+
// Organization invitations (scoped by org)
|
|
31
|
+
invitations: (locator: ProjectLocator) => [locator, "invitations"] as const,
|
|
32
|
+
|
|
33
|
+
// Organization roles (scoped by org)
|
|
34
|
+
organizationRoles: (locator: ProjectLocator) =>
|
|
35
|
+
[locator, "organization-roles"] as const,
|
|
36
|
+
|
|
37
|
+
// Connections (scoped by project)
|
|
38
|
+
connections: (locator: ProjectLocator) => [locator, "connections"] as const,
|
|
39
|
+
connectionsByBinding: (locator: ProjectLocator, binding: string) =>
|
|
40
|
+
[locator, "connections", `binding:${binding}`] as const,
|
|
41
|
+
connection: (locator: ProjectLocator, id: string) =>
|
|
42
|
+
[locator, "connection", id] as const,
|
|
43
|
+
|
|
44
|
+
isMCPAuthenticated: (url: string, token: string | null) =>
|
|
45
|
+
["is-mcp-authenticated", url, token] as const,
|
|
46
|
+
|
|
47
|
+
// MCP tools (scoped by URL and optional token)
|
|
48
|
+
mcpTools: (url: string, token?: string | null) =>
|
|
49
|
+
["mcp", "tools", url, token] as const,
|
|
50
|
+
|
|
51
|
+
// MCP client (scoped by orgId, connectionId, token, and meshUrl)
|
|
52
|
+
mcpClient: (
|
|
53
|
+
orgId: string,
|
|
54
|
+
connectionId: string,
|
|
55
|
+
token: string,
|
|
56
|
+
meshUrl: string,
|
|
57
|
+
) => ["mcp", "client", orgId, connectionId, token, meshUrl] as const,
|
|
58
|
+
|
|
59
|
+
// MCP client-based queries (scoped by client instance)
|
|
60
|
+
mcpToolsList: (client: unknown) =>
|
|
61
|
+
["mcp", "client", client, "tools"] as const,
|
|
62
|
+
mcpResourcesList: (client: unknown) =>
|
|
63
|
+
["mcp", "client", client, "resources"] as const,
|
|
64
|
+
mcpPromptsList: (client: unknown) =>
|
|
65
|
+
["mcp", "client", client, "prompts"] as const,
|
|
66
|
+
mcpReadResource: (client: unknown, uri: string) =>
|
|
67
|
+
["mcp", "client", client, "resource", uri] as const,
|
|
68
|
+
mcpGetPrompt: (client: unknown, name: string, argsKey: string) =>
|
|
69
|
+
["mcp", "client", client, "prompt", name, argsKey] as const,
|
|
70
|
+
mcpToolCall: (client: unknown, toolName: string, argsKey: string) =>
|
|
71
|
+
["mcp", "client", client, "tool-call", toolName, argsKey] as const,
|
|
72
|
+
|
|
73
|
+
organizationSettings: (organizationId: string) =>
|
|
74
|
+
["organization-settings", organizationId] as const,
|
|
75
|
+
|
|
76
|
+
// Active organization
|
|
77
|
+
activeOrganization: (org: string | undefined) =>
|
|
78
|
+
["activeOrganization", org] as const,
|
|
79
|
+
|
|
80
|
+
// Models list (scoped by organization)
|
|
81
|
+
modelsList: (orgId: string) => ["models-list", orgId] as const,
|
|
82
|
+
|
|
83
|
+
// Collections (scoped by connection)
|
|
84
|
+
connectionCollections: (connectionId: string) =>
|
|
85
|
+
[connectionId, "collections", "discovery"] as const,
|
|
86
|
+
|
|
87
|
+
// Tool call results (generic caching for MCP tool calls)
|
|
88
|
+
// scope is required - scopes the cache (connectionId for connection-scoped, locator for org/project-scoped)
|
|
89
|
+
toolCall: (scope: string, toolName: string, paramsKey: string) =>
|
|
90
|
+
["tool-call", scope, toolName, paramsKey] as const,
|
|
91
|
+
|
|
92
|
+
// Collection items (scoped by connection and collection name)
|
|
93
|
+
collectionItems: (connectionId: string, collectionName: string) =>
|
|
94
|
+
["collection", connectionId, collectionName] as const,
|
|
95
|
+
|
|
96
|
+
// Collection CRUD queries (scoped by orgId, scopeKey, client, and collection name)
|
|
97
|
+
// orgId: organization ID
|
|
98
|
+
// scopeKey: connectionId for connection-scoped tools, virtualMcpId for virtual-mcp-scoped, etc.
|
|
99
|
+
// client: MCP client instance for cache isolation
|
|
100
|
+
// Base prefix for invalidating all collection variants
|
|
101
|
+
collection: (orgId: string, scopeKey: string, collectionName: string) =>
|
|
102
|
+
[orgId, scopeKey, "collection", collectionName] as const,
|
|
103
|
+
// Item query
|
|
104
|
+
collectionItem: (
|
|
105
|
+
client: unknown,
|
|
106
|
+
orgId: string,
|
|
107
|
+
scopeKey: string,
|
|
108
|
+
collectionName: string,
|
|
109
|
+
itemId: string,
|
|
110
|
+
) => [client, orgId, scopeKey, "collection", collectionName, itemId] as const,
|
|
111
|
+
// List query
|
|
112
|
+
collectionList: (
|
|
113
|
+
client: unknown,
|
|
114
|
+
orgId: string,
|
|
115
|
+
scopeKey: string,
|
|
116
|
+
collectionName: string,
|
|
117
|
+
paramsKey: string,
|
|
118
|
+
) =>
|
|
119
|
+
[
|
|
120
|
+
client,
|
|
121
|
+
orgId,
|
|
122
|
+
scopeKey,
|
|
123
|
+
"collection",
|
|
124
|
+
collectionName,
|
|
125
|
+
"list",
|
|
126
|
+
paramsKey,
|
|
127
|
+
] as const,
|
|
128
|
+
// Infinite list query
|
|
129
|
+
collectionListInfinite: (
|
|
130
|
+
client: unknown,
|
|
131
|
+
orgId: string,
|
|
132
|
+
scopeKey: string,
|
|
133
|
+
collectionName: string,
|
|
134
|
+
paramsKey: string,
|
|
135
|
+
) =>
|
|
136
|
+
[
|
|
137
|
+
client,
|
|
138
|
+
orgId,
|
|
139
|
+
scopeKey,
|
|
140
|
+
"collection",
|
|
141
|
+
collectionName,
|
|
142
|
+
"list-infinite",
|
|
143
|
+
paramsKey,
|
|
144
|
+
] as const,
|
|
145
|
+
|
|
146
|
+
// GitHub README (scoped by owner and repo)
|
|
147
|
+
githubReadme: (
|
|
148
|
+
owner: string | null | undefined,
|
|
149
|
+
repo: string | null | undefined,
|
|
150
|
+
) => ["github-readme", owner, repo] as const,
|
|
151
|
+
|
|
152
|
+
// Monitoring queries
|
|
153
|
+
monitoringStats: () => ["monitoring", "stats"] as const,
|
|
154
|
+
monitoringLogs: (filters: {
|
|
155
|
+
connectionId?: string;
|
|
156
|
+
toolName?: string;
|
|
157
|
+
isError?: boolean;
|
|
158
|
+
limit?: number;
|
|
159
|
+
offset?: number;
|
|
160
|
+
}) => ["monitoring", "logs", filters] as const,
|
|
161
|
+
monitoringLogsInfinite: (locator: string, paramsKey: string) =>
|
|
162
|
+
["monitoring", "logs-infinite", locator, paramsKey] as const,
|
|
163
|
+
|
|
164
|
+
// Virtual MCP prompts (for ice breakers in chat)
|
|
165
|
+
virtualMcpPrompts: (virtualMcpId: string) =>
|
|
166
|
+
["virtual-mcp", virtualMcpId, "prompts"] as const,
|
|
167
|
+
|
|
168
|
+
// Connection prompts (for virtual MCP settings)
|
|
169
|
+
connectionPrompts: (connectionId: string) =>
|
|
170
|
+
["connection", connectionId, "prompts"] as const,
|
|
171
|
+
|
|
172
|
+
// Connection resources (for virtual MCP settings)
|
|
173
|
+
connectionResources: (connectionId: string) =>
|
|
174
|
+
["connection", connectionId, "resources"] as const,
|
|
175
|
+
|
|
176
|
+
// User data
|
|
177
|
+
user: (userId: string) => ["user", userId] as const,
|
|
178
|
+
} as const;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import {
|
|
3
|
+
StreamableHTTPClientTransport as BaseStreamableHTTPClientTransport,
|
|
4
|
+
type StreamableHTTPClientTransportOptions,
|
|
5
|
+
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Client-side HTTP transport for MCP connections.
|
|
9
|
+
* Extends StreamableHTTPClientTransport with support for mocking certain MCP protocol messages.
|
|
10
|
+
*/
|
|
11
|
+
export class StreamableHTTPClientTransport extends BaseStreamableHTTPClientTransport {
|
|
12
|
+
constructor(url: URL, opts?: StreamableHTTPClientTransportOptions) {
|
|
13
|
+
super(url, opts);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override send(
|
|
17
|
+
message: JSONRPCMessage,
|
|
18
|
+
options?: {
|
|
19
|
+
resumptionToken?: string;
|
|
20
|
+
onresumptiontoken?: (token: string) => void;
|
|
21
|
+
},
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
const mockAction = getMockActionFor(message);
|
|
24
|
+
if (mockAction?.type === "emit") {
|
|
25
|
+
this.onmessage?.(mockAction.message);
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
}
|
|
28
|
+
if (mockAction?.type === "suppress") {
|
|
29
|
+
return Promise.resolve();
|
|
30
|
+
}
|
|
31
|
+
return super.send(message, options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type MockAction =
|
|
36
|
+
| { type: "emit"; message: JSONRPCMessage }
|
|
37
|
+
| { type: "suppress" };
|
|
38
|
+
|
|
39
|
+
function getMockActionFor(message: JSONRPCMessage): MockAction | null {
|
|
40
|
+
const m = message;
|
|
41
|
+
if (!m || typeof m !== "object" || !("method" in m)) return null;
|
|
42
|
+
|
|
43
|
+
switch (m.method) {
|
|
44
|
+
case "initialize": {
|
|
45
|
+
const protocolVersion = m?.params?.protocolVersion;
|
|
46
|
+
if (!protocolVersion) return null;
|
|
47
|
+
return {
|
|
48
|
+
type: "emit",
|
|
49
|
+
message: {
|
|
50
|
+
result: {
|
|
51
|
+
protocolVersion,
|
|
52
|
+
capabilities: {
|
|
53
|
+
tools: {},
|
|
54
|
+
resources: {},
|
|
55
|
+
prompts: {},
|
|
56
|
+
tasks: {
|
|
57
|
+
list: {},
|
|
58
|
+
cancel: {},
|
|
59
|
+
requests: { tool: { call: {} } },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
serverInfo: { name: "mesh-virtual-mcp", version: "1.0.0" },
|
|
63
|
+
},
|
|
64
|
+
jsonrpc: m.jsonrpc ?? "2.0",
|
|
65
|
+
// @ts-expect-error - id is not typed
|
|
66
|
+
id: m.id,
|
|
67
|
+
} as JSONRPCMessage,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
case "notifications/roots/list_changed":
|
|
71
|
+
case "notifications/initialized":
|
|
72
|
+
case "notifications/cancelled":
|
|
73
|
+
case "notifications/progress": {
|
|
74
|
+
return { type: "suppress" };
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection Entity Schema
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for connection types.
|
|
5
|
+
* Uses snake_case field names matching the database schema directly.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* OAuth configuration schema for downstream MCP
|
|
12
|
+
*/
|
|
13
|
+
const OAuthConfigSchema = z.object({
|
|
14
|
+
authorizationEndpoint: z.string().url(),
|
|
15
|
+
tokenEndpoint: z.string().url(),
|
|
16
|
+
introspectionEndpoint: z.string().url().optional(),
|
|
17
|
+
clientId: z.string(),
|
|
18
|
+
clientSecret: z.string().optional(),
|
|
19
|
+
scopes: z.array(z.string()),
|
|
20
|
+
grantType: z.enum(["authorization_code", "client_credentials"]),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type OAuthConfig = z.infer<typeof OAuthConfigSchema>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tool definition schema from MCP discovery
|
|
27
|
+
*/
|
|
28
|
+
const ToolDefinitionSchema = z.object({
|
|
29
|
+
name: z.string(),
|
|
30
|
+
description: z.string().optional(),
|
|
31
|
+
inputSchema: z.record(z.string(), z.unknown()),
|
|
32
|
+
outputSchema: z.record(z.string(), z.unknown()).optional(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export type ToolDefinition = z.infer<typeof ToolDefinitionSchema>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Connection parameters - discriminated by connection_type
|
|
39
|
+
*
|
|
40
|
+
* HTTP/SSE/WebSocket: HTTP headers for requests
|
|
41
|
+
* STDIO: Environment variables + command config
|
|
42
|
+
*/
|
|
43
|
+
const HttpConnectionParametersSchema = z.object({
|
|
44
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const StdioConnectionParametersSchema = z.object({
|
|
48
|
+
command: z.string().describe("Command to run (e.g., 'npx', 'node')"),
|
|
49
|
+
args: z.array(z.string()).optional().describe("Command arguments"),
|
|
50
|
+
cwd: z.string().optional().describe("Working directory"),
|
|
51
|
+
envVars: z
|
|
52
|
+
.record(z.string(), z.string())
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Environment variables (encrypted in storage)"),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export type HttpConnectionParameters = z.infer<
|
|
58
|
+
typeof HttpConnectionParametersSchema
|
|
59
|
+
>;
|
|
60
|
+
export type StdioConnectionParameters = z.infer<
|
|
61
|
+
typeof StdioConnectionParametersSchema
|
|
62
|
+
>;
|
|
63
|
+
export type ConnectionParameters =
|
|
64
|
+
| HttpConnectionParameters
|
|
65
|
+
| StdioConnectionParameters;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Connection entity schema - single source of truth.
|
|
69
|
+
* Compliant with collections binding pattern.
|
|
70
|
+
*/
|
|
71
|
+
export const ConnectionEntitySchema = z.object({
|
|
72
|
+
// Base collection entity fields
|
|
73
|
+
id: z.string().describe("Unique identifier for the connection"),
|
|
74
|
+
title: z.string().describe("Human-readable name for the connection"),
|
|
75
|
+
created_at: z.string().describe("When the connection was created"),
|
|
76
|
+
updated_at: z.string().describe("When the connection was last updated"),
|
|
77
|
+
created_by: z.string().describe("User ID who created the connection"),
|
|
78
|
+
updated_by: z
|
|
79
|
+
.string()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("User ID who last updated the connection"),
|
|
82
|
+
|
|
83
|
+
// Connection-specific fields
|
|
84
|
+
organization_id: z
|
|
85
|
+
.string()
|
|
86
|
+
.describe("Organization ID this connection belongs to"),
|
|
87
|
+
description: z.string().nullable().describe("Description of the connection"),
|
|
88
|
+
icon: z.string().nullable().describe("Icon URL for the connection"),
|
|
89
|
+
app_name: z.string().nullable().describe("Associated app name"),
|
|
90
|
+
app_id: z.string().nullable().describe("Associated app ID"),
|
|
91
|
+
|
|
92
|
+
connection_type: z
|
|
93
|
+
.enum(["HTTP", "SSE", "Websocket", "STDIO", "VIRTUAL"])
|
|
94
|
+
.describe("Type of connection"),
|
|
95
|
+
connection_url: z
|
|
96
|
+
.string()
|
|
97
|
+
.nullable()
|
|
98
|
+
.describe(
|
|
99
|
+
"URL for HTTP/SSE/WebSocket connections. virtual://$id for VIRTUAL. Null for STDIO.",
|
|
100
|
+
),
|
|
101
|
+
connection_token: z
|
|
102
|
+
.string()
|
|
103
|
+
.nullable()
|
|
104
|
+
.describe("Authentication token (for HTTP connections)"),
|
|
105
|
+
connection_headers: z
|
|
106
|
+
.union([StdioConnectionParametersSchema, HttpConnectionParametersSchema])
|
|
107
|
+
.nullable()
|
|
108
|
+
.describe(
|
|
109
|
+
"Connection parameters. HTTP: { headers }. STDIO: { command, args, cwd, envVars }",
|
|
110
|
+
),
|
|
111
|
+
|
|
112
|
+
oauth_config: OAuthConfigSchema.nullable().describe("OAuth configuration"),
|
|
113
|
+
|
|
114
|
+
// New configuration fields (snake_case)
|
|
115
|
+
configuration_state: z
|
|
116
|
+
.record(z.string(), z.unknown())
|
|
117
|
+
.nullable()
|
|
118
|
+
.describe("Configuration state (decrypted)"),
|
|
119
|
+
configuration_scopes: z
|
|
120
|
+
.array(z.string())
|
|
121
|
+
.nullable()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe("Configuration scopes"),
|
|
124
|
+
|
|
125
|
+
metadata: z
|
|
126
|
+
.record(z.string(), z.unknown())
|
|
127
|
+
.nullable()
|
|
128
|
+
.describe("Additional metadata (includes repository info)"),
|
|
129
|
+
tools: z
|
|
130
|
+
.array(ToolDefinitionSchema)
|
|
131
|
+
.nullable()
|
|
132
|
+
.describe("Discovered tools from MCP"),
|
|
133
|
+
bindings: z.array(z.string()).nullable().describe("Detected bindings"),
|
|
134
|
+
|
|
135
|
+
status: z.enum(["active", "inactive", "error"]).describe("Current status"),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The connection entity type - use this everywhere instead of MCPConnection
|
|
140
|
+
*/
|
|
141
|
+
export type ConnectionEntity = z.infer<typeof ConnectionEntitySchema>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Input schema for creating connections
|
|
145
|
+
*/
|
|
146
|
+
export const ConnectionCreateDataSchema = ConnectionEntitySchema.omit({
|
|
147
|
+
created_at: true,
|
|
148
|
+
updated_at: true,
|
|
149
|
+
created_by: true,
|
|
150
|
+
updated_by: true,
|
|
151
|
+
organization_id: true,
|
|
152
|
+
tools: true,
|
|
153
|
+
bindings: true,
|
|
154
|
+
status: true,
|
|
155
|
+
}).partial({
|
|
156
|
+
id: true,
|
|
157
|
+
description: true,
|
|
158
|
+
icon: true,
|
|
159
|
+
app_name: true,
|
|
160
|
+
app_id: true,
|
|
161
|
+
connection_url: true,
|
|
162
|
+
connection_token: true,
|
|
163
|
+
connection_headers: true,
|
|
164
|
+
oauth_config: true,
|
|
165
|
+
configuration_state: true,
|
|
166
|
+
configuration_scopes: true,
|
|
167
|
+
metadata: true,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export type ConnectionCreateData = z.infer<typeof ConnectionCreateDataSchema>;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Input schema for updating connections
|
|
174
|
+
*/
|
|
175
|
+
export const ConnectionUpdateDataSchema = ConnectionEntitySchema.partial();
|
|
176
|
+
|
|
177
|
+
export type ConnectionUpdateData = z.infer<typeof ConnectionUpdateDataSchema>;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Type guard to check if parameters are STDIO type
|
|
181
|
+
*/
|
|
182
|
+
export function isStdioParameters(
|
|
183
|
+
params: ConnectionParameters | null | undefined,
|
|
184
|
+
): params is StdioConnectionParameters {
|
|
185
|
+
return params !== null && params !== undefined && "command" in params;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Parse virtual MCP ID from virtual:// URL
|
|
190
|
+
* @returns The virtual MCP ID or null if not a virtual URL
|
|
191
|
+
*/
|
|
192
|
+
export function parseVirtualUrl(url: string | null | undefined): string | null {
|
|
193
|
+
if (!url || !url.startsWith("virtual://")) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
return url.replace("virtual://", "");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Build virtual:// URL from virtual MCP ID
|
|
201
|
+
*/
|
|
202
|
+
export function buildVirtualUrl(virtualMcpId: string): string {
|
|
203
|
+
return `virtual://${virtualMcpId}`;
|
|
204
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ConnectionEntitySchema,
|
|
3
|
+
ConnectionCreateDataSchema,
|
|
4
|
+
ConnectionUpdateDataSchema,
|
|
5
|
+
isStdioParameters,
|
|
6
|
+
parseVirtualUrl,
|
|
7
|
+
buildVirtualUrl,
|
|
8
|
+
type ConnectionEntity,
|
|
9
|
+
type ConnectionCreateData,
|
|
10
|
+
type ConnectionUpdateData,
|
|
11
|
+
type ConnectionParameters,
|
|
12
|
+
type HttpConnectionParameters,
|
|
13
|
+
type StdioConnectionParameters,
|
|
14
|
+
type OAuthConfig,
|
|
15
|
+
type ToolDefinition,
|
|
16
|
+
} from "./connection";
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
VirtualMCPEntitySchema,
|
|
20
|
+
VirtualMCPCreateDataSchema,
|
|
21
|
+
VirtualMCPUpdateDataSchema,
|
|
22
|
+
type VirtualMCPEntity,
|
|
23
|
+
type VirtualMCPCreateData,
|
|
24
|
+
type VirtualMCPUpdateData,
|
|
25
|
+
type VirtualMCPConnection,
|
|
26
|
+
type ToolSelectionMode,
|
|
27
|
+
} from "./virtual-mcp";
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Virtual MCP Entity Schema
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for virtual MCP types.
|
|
5
|
+
* Uses snake_case field names matching the database schema directly.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Tool selection mode schema
|
|
12
|
+
* - "inclusion": Include selected tools/connections (default behavior)
|
|
13
|
+
* - "exclusion": Exclude selected tools/connections (inverse filter)
|
|
14
|
+
*/
|
|
15
|
+
const ToolSelectionModeSchema = z
|
|
16
|
+
.enum(["inclusion", "exclusion"])
|
|
17
|
+
.describe(
|
|
18
|
+
"Tool selection mode: 'inclusion' = include selected (default), 'exclusion' = exclude selected",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export type ToolSelectionMode = z.infer<typeof ToolSelectionModeSchema>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Virtual MCP connection schema - defines which connection and tools/resources/prompts are included/excluded
|
|
25
|
+
*/
|
|
26
|
+
const VirtualMCPConnectionSchema = z.object({
|
|
27
|
+
connection_id: z.string().describe("Connection ID"),
|
|
28
|
+
selected_tools: z
|
|
29
|
+
.array(z.string())
|
|
30
|
+
.nullable()
|
|
31
|
+
.describe(
|
|
32
|
+
"Selected tool names. With 'inclusion' mode: null = all tools included. With 'exclusion' mode: null = entire connection excluded",
|
|
33
|
+
),
|
|
34
|
+
selected_resources: z
|
|
35
|
+
.array(z.string())
|
|
36
|
+
.nullable()
|
|
37
|
+
.describe(
|
|
38
|
+
"Selected resource URIs or patterns. Supports * and ** wildcards for pattern matching. With 'inclusion' mode: null = all resources included.",
|
|
39
|
+
),
|
|
40
|
+
selected_prompts: z
|
|
41
|
+
.array(z.string())
|
|
42
|
+
.nullable()
|
|
43
|
+
.describe(
|
|
44
|
+
"Selected prompt names. With 'inclusion' mode: null = all prompts included. With 'exclusion' mode: null = entire connection excluded.",
|
|
45
|
+
),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export type VirtualMCPConnection = z.infer<typeof VirtualMCPConnectionSchema>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Virtual MCP entity schema - single source of truth
|
|
52
|
+
* Compliant with collections binding pattern
|
|
53
|
+
*/
|
|
54
|
+
export const VirtualMCPEntitySchema = z.object({
|
|
55
|
+
// Base collection entity fields
|
|
56
|
+
id: z.string().describe("Unique identifier for the virtual MCP"),
|
|
57
|
+
title: z.string().describe("Human-readable name for the virtual MCP"),
|
|
58
|
+
description: z.string().nullable().describe("Description of the virtual MCP"),
|
|
59
|
+
icon: z
|
|
60
|
+
.string()
|
|
61
|
+
.nullable()
|
|
62
|
+
.optional()
|
|
63
|
+
.describe("Icon URL for the virtual MCP"),
|
|
64
|
+
created_at: z.string().describe("When the virtual MCP was created"),
|
|
65
|
+
updated_at: z.string().describe("When the virtual MCP was last updated"),
|
|
66
|
+
created_by: z.string().describe("User ID who created the virtual MCP"),
|
|
67
|
+
updated_by: z
|
|
68
|
+
.string()
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("User ID who last updated the virtual MCP"),
|
|
71
|
+
|
|
72
|
+
// Virtual MCP-specific fields
|
|
73
|
+
organization_id: z
|
|
74
|
+
.string()
|
|
75
|
+
.describe("Organization ID this virtual MCP belongs to"),
|
|
76
|
+
tool_selection_mode: ToolSelectionModeSchema.describe(
|
|
77
|
+
"Tool selection mode: 'inclusion' = include selected, 'exclusion' = exclude selected",
|
|
78
|
+
),
|
|
79
|
+
status: z.enum(["active", "inactive"]).describe("Current status"),
|
|
80
|
+
// Metadata (stored in connections.metadata)
|
|
81
|
+
metadata: z
|
|
82
|
+
.object({
|
|
83
|
+
instructions: z.string().optional().describe("MCP server instructions"),
|
|
84
|
+
})
|
|
85
|
+
.nullable()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe("Additional metadata including MCP server instructions"),
|
|
88
|
+
// Nested connections
|
|
89
|
+
connections: z
|
|
90
|
+
.array(VirtualMCPConnectionSchema)
|
|
91
|
+
.describe(
|
|
92
|
+
"Connections with their selected tools (behavior depends on tool_selection_mode)",
|
|
93
|
+
),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The virtual MCP entity type
|
|
98
|
+
*/
|
|
99
|
+
export type VirtualMCPEntity = z.infer<typeof VirtualMCPEntitySchema>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Input schema for creating virtual MCPs
|
|
103
|
+
*/
|
|
104
|
+
export const VirtualMCPCreateDataSchema = z.object({
|
|
105
|
+
title: z.string().min(1).max(255).describe("Name for the virtual MCP"),
|
|
106
|
+
description: z
|
|
107
|
+
.string()
|
|
108
|
+
.nullable()
|
|
109
|
+
.optional()
|
|
110
|
+
.describe("Optional description"),
|
|
111
|
+
tool_selection_mode: ToolSelectionModeSchema.optional()
|
|
112
|
+
.default("inclusion")
|
|
113
|
+
.describe("Tool selection mode (defaults to 'inclusion')"),
|
|
114
|
+
icon: z.string().nullable().optional().describe("Optional icon URL"),
|
|
115
|
+
status: z
|
|
116
|
+
.enum(["active", "inactive"])
|
|
117
|
+
.optional()
|
|
118
|
+
.default("active")
|
|
119
|
+
.describe("Initial status"),
|
|
120
|
+
metadata: z
|
|
121
|
+
.object({
|
|
122
|
+
instructions: z.string().optional().describe("MCP server instructions"),
|
|
123
|
+
})
|
|
124
|
+
.nullable()
|
|
125
|
+
.optional()
|
|
126
|
+
.describe("Additional metadata including MCP server instructions"),
|
|
127
|
+
connections: z
|
|
128
|
+
.array(
|
|
129
|
+
z.object({
|
|
130
|
+
connection_id: z.string().describe("Connection ID"),
|
|
131
|
+
selected_tools: z
|
|
132
|
+
.array(z.string())
|
|
133
|
+
.nullable()
|
|
134
|
+
.optional()
|
|
135
|
+
.describe(
|
|
136
|
+
"Selected tool names (null/undefined = all tools or full exclusion)",
|
|
137
|
+
),
|
|
138
|
+
selected_resources: z
|
|
139
|
+
.array(z.string())
|
|
140
|
+
.nullable()
|
|
141
|
+
.optional()
|
|
142
|
+
.describe(
|
|
143
|
+
"Selected resource URIs or patterns with * and ** wildcards (null/undefined = all resources)",
|
|
144
|
+
),
|
|
145
|
+
selected_prompts: z
|
|
146
|
+
.array(z.string())
|
|
147
|
+
.nullable()
|
|
148
|
+
.optional()
|
|
149
|
+
.describe(
|
|
150
|
+
"Selected prompt names (null/undefined = all prompts or full exclusion)",
|
|
151
|
+
),
|
|
152
|
+
}),
|
|
153
|
+
)
|
|
154
|
+
.describe(
|
|
155
|
+
"Connections to include/exclude (can be empty for exclusion mode)",
|
|
156
|
+
),
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
export type VirtualMCPCreateData = z.infer<typeof VirtualMCPCreateDataSchema>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Input schema for updating virtual MCPs
|
|
163
|
+
*/
|
|
164
|
+
export const VirtualMCPUpdateDataSchema = z.object({
|
|
165
|
+
title: z.string().min(1).max(255).optional().describe("New name"),
|
|
166
|
+
description: z
|
|
167
|
+
.string()
|
|
168
|
+
.nullable()
|
|
169
|
+
.optional()
|
|
170
|
+
.describe("New description (null to clear)"),
|
|
171
|
+
tool_selection_mode: ToolSelectionModeSchema.optional().describe(
|
|
172
|
+
"New tool selection mode",
|
|
173
|
+
),
|
|
174
|
+
icon: z
|
|
175
|
+
.string()
|
|
176
|
+
.nullable()
|
|
177
|
+
.optional()
|
|
178
|
+
.describe("New icon URL (null to clear)"),
|
|
179
|
+
status: z.enum(["active", "inactive"]).optional().describe("New status"),
|
|
180
|
+
metadata: z
|
|
181
|
+
.object({
|
|
182
|
+
instructions: z.string().optional().describe("MCP server instructions"),
|
|
183
|
+
})
|
|
184
|
+
.nullable()
|
|
185
|
+
.optional()
|
|
186
|
+
.describe("Additional metadata including MCP server instructions"),
|
|
187
|
+
connections: z
|
|
188
|
+
.array(
|
|
189
|
+
z.object({
|
|
190
|
+
connection_id: z.string().describe("Connection ID"),
|
|
191
|
+
selected_tools: z
|
|
192
|
+
.array(z.string())
|
|
193
|
+
.nullable()
|
|
194
|
+
.optional()
|
|
195
|
+
.describe(
|
|
196
|
+
"Selected tool names (null/undefined = all tools or full exclusion)",
|
|
197
|
+
),
|
|
198
|
+
selected_resources: z
|
|
199
|
+
.array(z.string())
|
|
200
|
+
.nullable()
|
|
201
|
+
.optional()
|
|
202
|
+
.describe(
|
|
203
|
+
"Selected resource URIs or patterns with * and ** wildcards (null/undefined = all resources)",
|
|
204
|
+
),
|
|
205
|
+
selected_prompts: z
|
|
206
|
+
.array(z.string())
|
|
207
|
+
.nullable()
|
|
208
|
+
.optional()
|
|
209
|
+
.describe(
|
|
210
|
+
"Selected prompt names (null/undefined = all prompts or full exclusion)",
|
|
211
|
+
),
|
|
212
|
+
}),
|
|
213
|
+
)
|
|
214
|
+
.optional()
|
|
215
|
+
.describe("New connections (replaces existing)"),
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
export type VirtualMCPUpdateData = z.infer<typeof VirtualMCPUpdateDataSchema>;
|