@dexto/server 1.2.5
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 +44 -0
- package/dist/a2a/adapters/index.cjs +42 -0
- package/dist/a2a/adapters/index.d.ts +10 -0
- package/dist/a2a/adapters/index.d.ts.map +1 -0
- package/dist/a2a/adapters/index.js +12 -0
- package/dist/a2a/adapters/message.cjs +193 -0
- package/dist/a2a/adapters/message.d.ts +50 -0
- package/dist/a2a/adapters/message.d.ts.map +1 -0
- package/dist/a2a/adapters/message.js +167 -0
- package/dist/a2a/adapters/state.cjs +57 -0
- package/dist/a2a/adapters/state.d.ts +36 -0
- package/dist/a2a/adapters/state.d.ts.map +1 -0
- package/dist/a2a/adapters/state.js +32 -0
- package/dist/a2a/adapters/task-view.cjs +85 -0
- package/dist/a2a/adapters/task-view.d.ts +58 -0
- package/dist/a2a/adapters/task-view.d.ts.map +1 -0
- package/dist/a2a/adapters/task-view.js +60 -0
- package/dist/a2a/index.cjs +51 -0
- package/dist/a2a/index.d.ts +15 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +30 -0
- package/dist/a2a/jsonrpc/index.cjs +38 -0
- package/dist/a2a/jsonrpc/index.d.ts +11 -0
- package/dist/a2a/jsonrpc/index.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/index.js +10 -0
- package/dist/a2a/jsonrpc/methods.cjs +183 -0
- package/dist/a2a/jsonrpc/methods.d.ts +110 -0
- package/dist/a2a/jsonrpc/methods.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/methods.js +159 -0
- package/dist/a2a/jsonrpc/server.cjs +199 -0
- package/dist/a2a/jsonrpc/server.d.ts +100 -0
- package/dist/a2a/jsonrpc/server.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/server.js +175 -0
- package/dist/a2a/jsonrpc/types.cjs +47 -0
- package/dist/a2a/jsonrpc/types.d.ts +91 -0
- package/dist/a2a/jsonrpc/types.d.ts.map +1 -0
- package/dist/a2a/jsonrpc/types.js +21 -0
- package/dist/a2a/types.cjs +16 -0
- package/dist/a2a/types.d.ts +250 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +0 -0
- package/dist/approval/approval-coordinator.cjs +87 -0
- package/dist/approval/approval-coordinator.d.ts +52 -0
- package/dist/approval/approval-coordinator.d.ts.map +1 -0
- package/dist/approval/approval-coordinator.js +63 -0
- package/dist/approval/manual-approval-handler.cjs +100 -0
- package/dist/approval/manual-approval-handler.d.ts +32 -0
- package/dist/approval/manual-approval-handler.d.ts.map +1 -0
- package/dist/approval/manual-approval-handler.js +76 -0
- package/dist/events/a2a-sse-subscriber.cjs +271 -0
- package/dist/events/a2a-sse-subscriber.d.ts +94 -0
- package/dist/events/a2a-sse-subscriber.d.ts.map +1 -0
- package/dist/events/a2a-sse-subscriber.js +247 -0
- package/dist/events/types.cjs +16 -0
- package/dist/events/types.d.ts +15 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +0 -0
- package/dist/events/webhook-subscriber.cjs +301 -0
- package/dist/events/webhook-subscriber.d.ts +64 -0
- package/dist/events/webhook-subscriber.d.ts.map +1 -0
- package/dist/events/webhook-subscriber.js +269 -0
- package/dist/events/webhook-types.cjs +16 -0
- package/dist/events/webhook-types.d.ts +91 -0
- package/dist/events/webhook-types.d.ts.map +1 -0
- package/dist/events/webhook-types.js +0 -0
- package/dist/hono/__tests__/test-fixtures.cjs +236 -0
- package/dist/hono/__tests__/test-fixtures.d.ts +65 -0
- package/dist/hono/__tests__/test-fixtures.d.ts.map +1 -0
- package/dist/hono/__tests__/test-fixtures.js +197 -0
- package/dist/hono/index.cjs +166 -0
- package/dist/hono/index.d.ts +2783 -0
- package/dist/hono/index.d.ts.map +1 -0
- package/dist/hono/index.js +141 -0
- package/dist/hono/middleware/auth.cjs +75 -0
- package/dist/hono/middleware/auth.d.ts +3 -0
- package/dist/hono/middleware/auth.d.ts.map +1 -0
- package/dist/hono/middleware/auth.js +51 -0
- package/dist/hono/middleware/cors.cjs +57 -0
- package/dist/hono/middleware/cors.d.ts +9 -0
- package/dist/hono/middleware/cors.d.ts.map +1 -0
- package/dist/hono/middleware/cors.js +33 -0
- package/dist/hono/middleware/error.cjs +131 -0
- package/dist/hono/middleware/error.d.ts +5 -0
- package/dist/hono/middleware/error.d.ts.map +1 -0
- package/dist/hono/middleware/error.js +105 -0
- package/dist/hono/middleware/redaction.cjs +45 -0
- package/dist/hono/middleware/redaction.d.ts +4 -0
- package/dist/hono/middleware/redaction.d.ts.map +1 -0
- package/dist/hono/middleware/redaction.js +20 -0
- package/dist/hono/node/index.cjs +139 -0
- package/dist/hono/node/index.d.ts +19 -0
- package/dist/hono/node/index.d.ts.map +1 -0
- package/dist/hono/node/index.js +115 -0
- package/dist/hono/routes/a2a-jsonrpc.cjs +119 -0
- package/dist/hono/routes/a2a-jsonrpc.d.ts +46 -0
- package/dist/hono/routes/a2a-jsonrpc.d.ts.map +1 -0
- package/dist/hono/routes/a2a-jsonrpc.js +95 -0
- package/dist/hono/routes/a2a-tasks.cjs +315 -0
- package/dist/hono/routes/a2a-tasks.d.ts +530 -0
- package/dist/hono/routes/a2a-tasks.d.ts.map +1 -0
- package/dist/hono/routes/a2a-tasks.js +291 -0
- package/dist/hono/routes/a2a.cjs +36 -0
- package/dist/hono/routes/a2a.d.ts +4 -0
- package/dist/hono/routes/a2a.d.ts.map +1 -0
- package/dist/hono/routes/a2a.js +12 -0
- package/dist/hono/routes/agents.cjs +735 -0
- package/dist/hono/routes/agents.d.ts +650 -0
- package/dist/hono/routes/agents.d.ts.map +1 -0
- package/dist/hono/routes/agents.js +711 -0
- package/dist/hono/routes/approvals.cjs +125 -0
- package/dist/hono/routes/approvals.d.ts +89 -0
- package/dist/hono/routes/approvals.d.ts.map +1 -0
- package/dist/hono/routes/approvals.js +101 -0
- package/dist/hono/routes/greeting.cjs +60 -0
- package/dist/hono/routes/greeting.d.ts +19 -0
- package/dist/hono/routes/greeting.d.ts.map +1 -0
- package/dist/hono/routes/greeting.js +36 -0
- package/dist/hono/routes/health.cjs +45 -0
- package/dist/hono/routes/health.d.ts +17 -0
- package/dist/hono/routes/health.d.ts.map +1 -0
- package/dist/hono/routes/health.js +21 -0
- package/dist/hono/routes/llm.cjs +298 -0
- package/dist/hono/routes/llm.d.ts +294 -0
- package/dist/hono/routes/llm.d.ts.map +1 -0
- package/dist/hono/routes/llm.js +287 -0
- package/dist/hono/routes/mcp.cjs +356 -0
- package/dist/hono/routes/mcp.d.ts +246 -0
- package/dist/hono/routes/mcp.d.ts.map +1 -0
- package/dist/hono/routes/mcp.js +332 -0
- package/dist/hono/routes/memory.cjs +192 -0
- package/dist/hono/routes/memory.d.ts +146 -0
- package/dist/hono/routes/memory.d.ts.map +1 -0
- package/dist/hono/routes/memory.js +168 -0
- package/dist/hono/routes/messages.cjs +320 -0
- package/dist/hono/routes/messages.d.ts +163 -0
- package/dist/hono/routes/messages.d.ts.map +1 -0
- package/dist/hono/routes/messages.js +296 -0
- package/dist/hono/routes/prompts.cjs +228 -0
- package/dist/hono/routes/prompts.d.ts +150 -0
- package/dist/hono/routes/prompts.d.ts.map +1 -0
- package/dist/hono/routes/prompts.js +204 -0
- package/dist/hono/routes/resources.cjs +110 -0
- package/dist/hono/routes/resources.d.ts +76 -0
- package/dist/hono/routes/resources.d.ts.map +1 -0
- package/dist/hono/routes/resources.js +86 -0
- package/dist/hono/routes/search.cjs +109 -0
- package/dist/hono/routes/search.d.ts +137 -0
- package/dist/hono/routes/search.d.ts.map +1 -0
- package/dist/hono/routes/search.js +85 -0
- package/dist/hono/routes/sessions.cjs +366 -0
- package/dist/hono/routes/sessions.d.ts +229 -0
- package/dist/hono/routes/sessions.d.ts.map +1 -0
- package/dist/hono/routes/sessions.js +342 -0
- package/dist/hono/routes/webhooks.cjs +228 -0
- package/dist/hono/routes/webhooks.d.ts +127 -0
- package/dist/hono/routes/webhooks.d.ts.map +1 -0
- package/dist/hono/routes/webhooks.js +204 -0
- package/dist/hono/schemas/responses.cjs +276 -0
- package/dist/hono/schemas/responses.d.ts +1418 -0
- package/dist/hono/schemas/responses.d.ts.map +1 -0
- package/dist/hono/schemas/responses.js +227 -0
- package/dist/hono/types.cjs +16 -0
- package/dist/hono/types.d.ts +6 -0
- package/dist/hono/types.d.ts.map +1 -0
- package/dist/hono/types.js +0 -0
- package/dist/index.cjs +38 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/mcp/mcp-handler.cjs +145 -0
- package/dist/mcp/mcp-handler.d.ts +14 -0
- package/dist/mcp/mcp-handler.d.ts.map +1 -0
- package/dist/mcp/mcp-handler.js +118 -0
- package/package.json +59 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
|
2
|
+
import {
|
|
3
|
+
LLM_REGISTRY,
|
|
4
|
+
LLM_PROVIDERS,
|
|
5
|
+
LLM_ROUTERS,
|
|
6
|
+
SUPPORTED_FILE_TYPES,
|
|
7
|
+
getSupportedRoutersForProvider,
|
|
8
|
+
supportsBaseURL,
|
|
9
|
+
isRouterSupportedForModel,
|
|
10
|
+
LLMUpdatesSchema
|
|
11
|
+
} from "@dexto/core";
|
|
12
|
+
import { getProviderKeyStatus, saveProviderApiKey } from "@dexto/agent-management";
|
|
13
|
+
import {
|
|
14
|
+
ProviderCatalogSchema,
|
|
15
|
+
ModelFlatSchema,
|
|
16
|
+
LLMConfigResponseSchema
|
|
17
|
+
} from "../schemas/responses.js";
|
|
18
|
+
const CurrentQuerySchema = z.object({
|
|
19
|
+
sessionId: z.string().optional().describe("Session identifier to retrieve session-specific LLM configuration")
|
|
20
|
+
}).describe("Query parameters for getting current LLM configuration");
|
|
21
|
+
const CatalogQuerySchema = z.object({
|
|
22
|
+
provider: z.union([z.string(), z.array(z.string())]).optional().transform(
|
|
23
|
+
(value) => Array.isArray(value) ? value : value ? value.split(",") : void 0
|
|
24
|
+
).describe("Comma-separated list of LLM providers to filter by"),
|
|
25
|
+
hasKey: z.union([z.literal("true"), z.literal("false"), z.literal("1"), z.literal("0")]).optional().transform(
|
|
26
|
+
(raw) => raw === "true" || raw === "1" ? true : raw === "false" || raw === "0" ? false : void 0
|
|
27
|
+
).describe("Filter by API key presence (true or false)"),
|
|
28
|
+
router: z.enum(LLM_ROUTERS).optional().describe("Filter by router type (vercel or in-built)"),
|
|
29
|
+
fileType: z.enum(SUPPORTED_FILE_TYPES).optional().describe("Filter by supported file type (audio, pdf, or image)"),
|
|
30
|
+
defaultOnly: z.union([z.literal("true"), z.literal("false"), z.literal("1"), z.literal("0")]).optional().transform(
|
|
31
|
+
(raw) => raw === "true" || raw === "1" ? true : raw === "false" || raw === "0" ? false : void 0
|
|
32
|
+
).describe("Include only default models (true or false)"),
|
|
33
|
+
mode: z.enum(["grouped", "flat"]).default("grouped").describe("Response format mode (grouped by provider or flat list)")
|
|
34
|
+
}).describe("Query parameters for filtering and formatting the LLM catalog");
|
|
35
|
+
const SaveKeySchema = z.object({
|
|
36
|
+
provider: z.enum(LLM_PROVIDERS).describe("LLM provider identifier (e.g., openai, anthropic)"),
|
|
37
|
+
apiKey: z.string().min(1, "API key is required").describe("API key for the provider (writeOnly - never returned in responses)").openapi({ writeOnly: true })
|
|
38
|
+
}).describe("Request body for saving a provider API key");
|
|
39
|
+
const SwitchLLMBodySchema = LLMUpdatesSchema.and(
|
|
40
|
+
z.object({
|
|
41
|
+
sessionId: z.string().optional().describe("Session identifier for session-specific LLM configuration")
|
|
42
|
+
})
|
|
43
|
+
).describe("LLM switch request body with optional session ID and LLM fields");
|
|
44
|
+
function createLlmRouter(getAgent) {
|
|
45
|
+
const app = new OpenAPIHono();
|
|
46
|
+
const currentRoute = createRoute({
|
|
47
|
+
method: "get",
|
|
48
|
+
path: "/llm/current",
|
|
49
|
+
summary: "Get Current LLM Config",
|
|
50
|
+
description: "Retrieves the current LLM configuration for the agent or a specific session",
|
|
51
|
+
tags: ["llm"],
|
|
52
|
+
request: { query: CurrentQuerySchema },
|
|
53
|
+
responses: {
|
|
54
|
+
200: {
|
|
55
|
+
description: "Current LLM config",
|
|
56
|
+
content: {
|
|
57
|
+
"application/json": {
|
|
58
|
+
schema: z.object({
|
|
59
|
+
config: LLMConfigResponseSchema.partial({
|
|
60
|
+
maxIterations: true,
|
|
61
|
+
router: true
|
|
62
|
+
}).extend({
|
|
63
|
+
displayName: z.string().optional().describe("Human-readable model display name")
|
|
64
|
+
})
|
|
65
|
+
}).describe("Response containing current LLM configuration")
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
const catalogRoute = createRoute({
|
|
72
|
+
method: "get",
|
|
73
|
+
path: "/llm/catalog",
|
|
74
|
+
summary: "LLM Catalog",
|
|
75
|
+
description: "Providers, models, capabilities, and API key status",
|
|
76
|
+
tags: ["llm"],
|
|
77
|
+
request: { query: CatalogQuerySchema },
|
|
78
|
+
responses: {
|
|
79
|
+
200: {
|
|
80
|
+
description: "LLM catalog",
|
|
81
|
+
content: {
|
|
82
|
+
"application/json": {
|
|
83
|
+
schema: z.union([
|
|
84
|
+
z.object({
|
|
85
|
+
providers: z.record(z.enum(LLM_PROVIDERS), ProviderCatalogSchema).describe(
|
|
86
|
+
"Providers grouped by ID with their models and capabilities"
|
|
87
|
+
)
|
|
88
|
+
}).strict().describe("Grouped catalog response (mode=grouped)"),
|
|
89
|
+
z.object({
|
|
90
|
+
models: z.array(ModelFlatSchema).describe(
|
|
91
|
+
"Flat list of all models with provider information"
|
|
92
|
+
)
|
|
93
|
+
}).strict().describe("Flat catalog response (mode=flat)")
|
|
94
|
+
]).describe(
|
|
95
|
+
"LLM catalog in grouped or flat format based on mode query parameter"
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const saveKeyRoute = createRoute({
|
|
103
|
+
method: "post",
|
|
104
|
+
path: "/llm/key",
|
|
105
|
+
summary: "Save Provider API Key",
|
|
106
|
+
description: "Stores an API key for a provider in .env and makes it available immediately",
|
|
107
|
+
tags: ["llm"],
|
|
108
|
+
request: { body: { content: { "application/json": { schema: SaveKeySchema } } } },
|
|
109
|
+
responses: {
|
|
110
|
+
200: {
|
|
111
|
+
description: "API key saved",
|
|
112
|
+
content: {
|
|
113
|
+
"application/json": {
|
|
114
|
+
schema: z.object({
|
|
115
|
+
ok: z.literal(true).describe("Operation success indicator"),
|
|
116
|
+
provider: z.enum(LLM_PROVIDERS).describe("Provider for which the key was saved"),
|
|
117
|
+
envVar: z.string().describe("Environment variable name where key was stored")
|
|
118
|
+
}).strict().describe("API key save response")
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
const switchRoute = createRoute({
|
|
125
|
+
method: "post",
|
|
126
|
+
path: "/llm/switch",
|
|
127
|
+
summary: "Switch LLM",
|
|
128
|
+
description: "Switches the LLM configuration for the agent or a specific session",
|
|
129
|
+
tags: ["llm"],
|
|
130
|
+
request: {
|
|
131
|
+
body: {
|
|
132
|
+
content: {
|
|
133
|
+
"application/json": {
|
|
134
|
+
schema: SwitchLLMBodySchema
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
responses: {
|
|
140
|
+
200: {
|
|
141
|
+
description: "LLM switch result",
|
|
142
|
+
content: {
|
|
143
|
+
"application/json": {
|
|
144
|
+
schema: z.object({
|
|
145
|
+
config: LLMConfigResponseSchema.describe(
|
|
146
|
+
"New LLM configuration with all defaults applied (apiKey omitted)"
|
|
147
|
+
),
|
|
148
|
+
sessionId: z.string().optional().describe("Session ID if session-specific switch")
|
|
149
|
+
}).describe("LLM switch result")
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return app.openapi(currentRoute, (ctx) => {
|
|
156
|
+
const agent = getAgent();
|
|
157
|
+
const { sessionId } = ctx.req.valid("query");
|
|
158
|
+
const currentConfig = sessionId ? agent.getEffectiveConfig(sessionId).llm : agent.getCurrentLLMConfig();
|
|
159
|
+
let displayName;
|
|
160
|
+
try {
|
|
161
|
+
const model = LLM_REGISTRY[currentConfig.provider]?.models.find(
|
|
162
|
+
(m) => m.name.toLowerCase() === String(currentConfig.model).toLowerCase()
|
|
163
|
+
);
|
|
164
|
+
displayName = model?.displayName || void 0;
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
const { apiKey, ...configWithoutKey } = currentConfig;
|
|
168
|
+
return ctx.json({
|
|
169
|
+
config: {
|
|
170
|
+
...configWithoutKey,
|
|
171
|
+
hasApiKey: !!apiKey,
|
|
172
|
+
...displayName && { displayName }
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}).openapi(catalogRoute, (ctx) => {
|
|
176
|
+
const queryParams = ctx.req.valid("query");
|
|
177
|
+
const providers = {};
|
|
178
|
+
for (const provider of LLM_PROVIDERS) {
|
|
179
|
+
const info = LLM_REGISTRY[provider];
|
|
180
|
+
const displayName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
181
|
+
const keyStatus = getProviderKeyStatus(provider);
|
|
182
|
+
providers[provider] = {
|
|
183
|
+
name: displayName,
|
|
184
|
+
hasApiKey: keyStatus.hasApiKey,
|
|
185
|
+
primaryEnvVar: keyStatus.envVar,
|
|
186
|
+
supportedRouters: getSupportedRoutersForProvider(provider),
|
|
187
|
+
supportsBaseURL: supportsBaseURL(provider),
|
|
188
|
+
models: info.models,
|
|
189
|
+
supportedFileTypes: info.supportedFileTypes
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
let filtered = { ...providers };
|
|
193
|
+
if (queryParams.provider && queryParams.provider.length > 0) {
|
|
194
|
+
const allowed = new Set(
|
|
195
|
+
queryParams.provider.filter(
|
|
196
|
+
(p) => LLM_PROVIDERS.includes(p)
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
const filteredByProvider = {};
|
|
200
|
+
for (const [id, catalog] of Object.entries(filtered)) {
|
|
201
|
+
if (allowed.has(id)) {
|
|
202
|
+
filteredByProvider[id] = catalog;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
filtered = filteredByProvider;
|
|
206
|
+
}
|
|
207
|
+
if (typeof queryParams.hasKey === "boolean") {
|
|
208
|
+
const byKey = {};
|
|
209
|
+
for (const [id, catalog] of Object.entries(filtered)) {
|
|
210
|
+
if (catalog.hasApiKey === queryParams.hasKey) {
|
|
211
|
+
byKey[id] = catalog;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
filtered = byKey;
|
|
215
|
+
}
|
|
216
|
+
if (queryParams.router) {
|
|
217
|
+
const byRouter = {};
|
|
218
|
+
for (const [id, catalog] of Object.entries(filtered)) {
|
|
219
|
+
if (!catalog.supportedRouters.includes(queryParams.router)) continue;
|
|
220
|
+
const models = catalog.models.filter(
|
|
221
|
+
(model) => isRouterSupportedForModel(
|
|
222
|
+
id,
|
|
223
|
+
model.name,
|
|
224
|
+
queryParams.router
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
if (models.length > 0) {
|
|
228
|
+
byRouter[id] = { ...catalog, models };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
filtered = byRouter;
|
|
232
|
+
}
|
|
233
|
+
if (queryParams.fileType) {
|
|
234
|
+
const byFileType = {};
|
|
235
|
+
for (const [id, catalog] of Object.entries(filtered)) {
|
|
236
|
+
const models = catalog.models.filter((model) => {
|
|
237
|
+
const modelTypes = Array.isArray(model.supportedFileTypes) && model.supportedFileTypes.length > 0 ? model.supportedFileTypes : catalog.supportedFileTypes || [];
|
|
238
|
+
return modelTypes.includes(queryParams.fileType);
|
|
239
|
+
});
|
|
240
|
+
if (models.length > 0) {
|
|
241
|
+
byFileType[id] = { ...catalog, models };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
filtered = byFileType;
|
|
245
|
+
}
|
|
246
|
+
if (queryParams.defaultOnly) {
|
|
247
|
+
const byDefault = {};
|
|
248
|
+
for (const [id, catalog] of Object.entries(filtered)) {
|
|
249
|
+
const models = catalog.models.filter((model) => model.default === true);
|
|
250
|
+
if (models.length > 0) {
|
|
251
|
+
byDefault[id] = { ...catalog, models };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
filtered = byDefault;
|
|
255
|
+
}
|
|
256
|
+
if (queryParams.mode === "flat") {
|
|
257
|
+
const flat = [];
|
|
258
|
+
for (const [id, catalog] of Object.entries(filtered)) {
|
|
259
|
+
for (const model of catalog.models) {
|
|
260
|
+
flat.push({ provider: id, ...model });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return ctx.json({ models: flat });
|
|
264
|
+
}
|
|
265
|
+
return ctx.json({ providers: filtered });
|
|
266
|
+
}).openapi(saveKeyRoute, async (ctx) => {
|
|
267
|
+
const { provider, apiKey } = ctx.req.valid("json");
|
|
268
|
+
const meta = await saveProviderApiKey(provider, apiKey, process.cwd());
|
|
269
|
+
return ctx.json({ ok: true, provider, envVar: meta.envVar });
|
|
270
|
+
}).openapi(switchRoute, async (ctx) => {
|
|
271
|
+
const agent = getAgent();
|
|
272
|
+
const raw = ctx.req.valid("json");
|
|
273
|
+
const { sessionId, ...llmUpdates } = raw;
|
|
274
|
+
const config = await agent.switchLLM(llmUpdates, sessionId);
|
|
275
|
+
const { apiKey, ...configWithoutKey } = config;
|
|
276
|
+
return ctx.json({
|
|
277
|
+
config: {
|
|
278
|
+
...configWithoutKey,
|
|
279
|
+
hasApiKey: !!apiKey
|
|
280
|
+
},
|
|
281
|
+
sessionId
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
export {
|
|
286
|
+
createLlmRouter
|
|
287
|
+
};
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var mcp_exports = {};
|
|
20
|
+
__export(mcp_exports, {
|
|
21
|
+
createMcpRouter: () => createMcpRouter
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(mcp_exports);
|
|
24
|
+
var import_zod_openapi = require("@hono/zod-openapi");
|
|
25
|
+
var import_core = require("@dexto/core");
|
|
26
|
+
var import_agent_management = require("@dexto/agent-management");
|
|
27
|
+
var import_responses = require("../schemas/responses.js");
|
|
28
|
+
const McpServerRequestSchema = import_zod_openapi.z.object({
|
|
29
|
+
name: import_zod_openapi.z.string().min(1, "Server name is required").describe("A unique name for the server"),
|
|
30
|
+
config: import_core.McpServerConfigSchema.describe("The server configuration object"),
|
|
31
|
+
persistToAgent: import_zod_openapi.z.boolean().optional().describe("If true, saves the server to agent configuration file")
|
|
32
|
+
}).describe("Request body for adding or updating an MCP server");
|
|
33
|
+
const ExecuteToolBodySchema = import_zod_openapi.z.record(import_zod_openapi.z.unknown()).describe(
|
|
34
|
+
"Tool execution parameters as JSON object. The specific fields depend on the tool being executed and are defined by the tool's inputSchema."
|
|
35
|
+
);
|
|
36
|
+
const ServerStatusResponseSchema = import_zod_openapi.z.object({
|
|
37
|
+
status: import_zod_openapi.z.string().describe("Connection status"),
|
|
38
|
+
name: import_zod_openapi.z.string().describe("Server name")
|
|
39
|
+
}).strict().describe("Server status response");
|
|
40
|
+
const ServerInfoSchema = import_zod_openapi.z.object({
|
|
41
|
+
id: import_zod_openapi.z.string().describe("Server identifier"),
|
|
42
|
+
name: import_zod_openapi.z.string().describe("Server name"),
|
|
43
|
+
status: import_zod_openapi.z.enum(["connected", "error", "disconnected"]).describe("Server status")
|
|
44
|
+
}).strict().describe("MCP server information");
|
|
45
|
+
const ServersListResponseSchema = import_zod_openapi.z.object({
|
|
46
|
+
servers: import_zod_openapi.z.array(ServerInfoSchema).describe("Array of server information")
|
|
47
|
+
}).strict().describe("List of MCP servers");
|
|
48
|
+
const JsonSchemaProperty = import_zod_openapi.z.object({
|
|
49
|
+
type: import_zod_openapi.z.enum(["string", "number", "integer", "boolean", "object", "array"]).optional().describe("Property type"),
|
|
50
|
+
description: import_zod_openapi.z.string().optional().describe("Property description"),
|
|
51
|
+
enum: import_zod_openapi.z.array(import_zod_openapi.z.union([import_zod_openapi.z.string(), import_zod_openapi.z.number(), import_zod_openapi.z.boolean()])).optional().describe("Enum values"),
|
|
52
|
+
default: import_zod_openapi.z.any().optional().describe("Default value")
|
|
53
|
+
}).passthrough().describe("JSON Schema property definition");
|
|
54
|
+
const ToolInputSchema = import_zod_openapi.z.object({
|
|
55
|
+
type: import_zod_openapi.z.literal("object").optional().describe('Schema type, always "object" when present'),
|
|
56
|
+
properties: import_zod_openapi.z.record(JsonSchemaProperty).optional().describe("Property definitions"),
|
|
57
|
+
required: import_zod_openapi.z.array(import_zod_openapi.z.string()).optional().describe("Required property names")
|
|
58
|
+
}).passthrough().describe("JSON Schema for tool input parameters");
|
|
59
|
+
const ToolInfoSchema = import_zod_openapi.z.object({
|
|
60
|
+
id: import_zod_openapi.z.string().describe("Tool identifier"),
|
|
61
|
+
name: import_zod_openapi.z.string().describe("Tool name"),
|
|
62
|
+
description: import_zod_openapi.z.string().describe("Tool description"),
|
|
63
|
+
inputSchema: ToolInputSchema.optional().describe("JSON Schema for tool input parameters")
|
|
64
|
+
}).strict().describe("Tool information");
|
|
65
|
+
const ToolsListResponseSchema = import_zod_openapi.z.object({
|
|
66
|
+
tools: import_zod_openapi.z.array(ToolInfoSchema).describe("Array of available tools")
|
|
67
|
+
}).strict().describe("List of tools from MCP server");
|
|
68
|
+
const DisconnectResponseSchema = import_zod_openapi.z.object({
|
|
69
|
+
status: import_zod_openapi.z.literal("disconnected").describe("Disconnection status"),
|
|
70
|
+
id: import_zod_openapi.z.string().describe("Server identifier")
|
|
71
|
+
}).strict().describe("Server disconnection response");
|
|
72
|
+
const RestartResponseSchema = import_zod_openapi.z.object({
|
|
73
|
+
status: import_zod_openapi.z.literal("restarted").describe("Restart status"),
|
|
74
|
+
id: import_zod_openapi.z.string().describe("Server identifier")
|
|
75
|
+
}).strict().describe("Server restart response");
|
|
76
|
+
const ToolExecutionResponseSchema = import_zod_openapi.z.object({
|
|
77
|
+
success: import_zod_openapi.z.boolean().describe("Whether tool execution succeeded"),
|
|
78
|
+
data: import_zod_openapi.z.any().optional().describe("Tool execution result data"),
|
|
79
|
+
error: import_zod_openapi.z.string().optional().describe("Error message if execution failed")
|
|
80
|
+
}).strict().describe("Tool execution response");
|
|
81
|
+
const ResourcesListResponseSchema = import_zod_openapi.z.object({
|
|
82
|
+
success: import_zod_openapi.z.boolean().describe("Success indicator"),
|
|
83
|
+
resources: import_zod_openapi.z.array(import_responses.ResourceSchema).describe("Array of available resources")
|
|
84
|
+
}).strict().describe("List of resources from MCP server");
|
|
85
|
+
const ResourceContentSchema = import_zod_openapi.z.object({
|
|
86
|
+
content: import_zod_openapi.z.any().describe("Resource content data")
|
|
87
|
+
}).strict().describe("Resource content wrapper");
|
|
88
|
+
const ResourceContentResponseSchema = import_zod_openapi.z.object({
|
|
89
|
+
success: import_zod_openapi.z.boolean().describe("Success indicator"),
|
|
90
|
+
data: ResourceContentSchema.describe("Resource content")
|
|
91
|
+
}).strict().describe("Resource content response");
|
|
92
|
+
function createMcpRouter(getAgent) {
|
|
93
|
+
const app = new import_zod_openapi.OpenAPIHono();
|
|
94
|
+
const addServerRoute = (0, import_zod_openapi.createRoute)({
|
|
95
|
+
method: "post",
|
|
96
|
+
path: "/mcp/servers",
|
|
97
|
+
summary: "Add MCP Server",
|
|
98
|
+
description: "Connects a new MCP server dynamically",
|
|
99
|
+
tags: ["mcp"],
|
|
100
|
+
request: { body: { content: { "application/json": { schema: McpServerRequestSchema } } } },
|
|
101
|
+
responses: {
|
|
102
|
+
200: {
|
|
103
|
+
description: "Server connected",
|
|
104
|
+
content: { "application/json": { schema: ServerStatusResponseSchema } }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const listServersRoute = (0, import_zod_openapi.createRoute)({
|
|
109
|
+
method: "get",
|
|
110
|
+
path: "/mcp/servers",
|
|
111
|
+
summary: "List MCP Servers",
|
|
112
|
+
description: "Gets a list of all connected and failed MCP servers",
|
|
113
|
+
tags: ["mcp"],
|
|
114
|
+
responses: {
|
|
115
|
+
200: {
|
|
116
|
+
description: "Servers list",
|
|
117
|
+
content: { "application/json": { schema: ServersListResponseSchema } }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const toolsRoute = (0, import_zod_openapi.createRoute)({
|
|
122
|
+
method: "get",
|
|
123
|
+
path: "/mcp/servers/{serverId}/tools",
|
|
124
|
+
summary: "List Server Tools",
|
|
125
|
+
description: "Retrieves the list of tools available on a specific MCP server",
|
|
126
|
+
tags: ["mcp"],
|
|
127
|
+
request: {
|
|
128
|
+
params: import_zod_openapi.z.object({ serverId: import_zod_openapi.z.string().describe("The ID of the MCP server") })
|
|
129
|
+
},
|
|
130
|
+
responses: {
|
|
131
|
+
200: {
|
|
132
|
+
description: "Tools list",
|
|
133
|
+
content: { "application/json": { schema: ToolsListResponseSchema } }
|
|
134
|
+
},
|
|
135
|
+
404: { description: "Not found" }
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const deleteServerRoute = (0, import_zod_openapi.createRoute)({
|
|
139
|
+
method: "delete",
|
|
140
|
+
path: "/mcp/servers/{serverId}",
|
|
141
|
+
summary: "Remove MCP Server",
|
|
142
|
+
description: "Disconnects and removes an MCP server",
|
|
143
|
+
tags: ["mcp"],
|
|
144
|
+
request: {
|
|
145
|
+
params: import_zod_openapi.z.object({ serverId: import_zod_openapi.z.string().describe("The ID of the MCP server") })
|
|
146
|
+
},
|
|
147
|
+
responses: {
|
|
148
|
+
200: {
|
|
149
|
+
description: "Disconnected",
|
|
150
|
+
content: { "application/json": { schema: DisconnectResponseSchema } }
|
|
151
|
+
},
|
|
152
|
+
404: { description: "Not found" }
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const restartServerRoute = (0, import_zod_openapi.createRoute)({
|
|
156
|
+
method: "post",
|
|
157
|
+
path: "/mcp/servers/{serverId}/restart",
|
|
158
|
+
summary: "Restart MCP Server",
|
|
159
|
+
description: "Restarts a connected MCP server",
|
|
160
|
+
tags: ["mcp"],
|
|
161
|
+
request: {
|
|
162
|
+
params: import_zod_openapi.z.object({ serverId: import_zod_openapi.z.string().describe("The ID of the MCP server") })
|
|
163
|
+
},
|
|
164
|
+
responses: {
|
|
165
|
+
200: {
|
|
166
|
+
description: "Server restarted",
|
|
167
|
+
content: { "application/json": { schema: RestartResponseSchema } }
|
|
168
|
+
},
|
|
169
|
+
404: { description: "Not found" }
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const execToolRoute = (0, import_zod_openapi.createRoute)({
|
|
173
|
+
method: "post",
|
|
174
|
+
path: "/mcp/servers/{serverId}/tools/{toolName}/execute",
|
|
175
|
+
summary: "Execute MCP Tool",
|
|
176
|
+
description: "Executes a tool on an MCP server directly",
|
|
177
|
+
tags: ["mcp"],
|
|
178
|
+
request: {
|
|
179
|
+
params: import_zod_openapi.z.object({
|
|
180
|
+
serverId: import_zod_openapi.z.string().describe("The ID of the MCP server"),
|
|
181
|
+
toolName: import_zod_openapi.z.string().describe("The name of the tool to execute")
|
|
182
|
+
}),
|
|
183
|
+
body: { content: { "application/json": { schema: ExecuteToolBodySchema } } }
|
|
184
|
+
},
|
|
185
|
+
responses: {
|
|
186
|
+
200: {
|
|
187
|
+
description: "Tool executed",
|
|
188
|
+
content: { "application/json": { schema: ToolExecutionResponseSchema } }
|
|
189
|
+
},
|
|
190
|
+
404: { description: "Not found" }
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
const listResourcesRoute = (0, import_zod_openapi.createRoute)({
|
|
194
|
+
method: "get",
|
|
195
|
+
path: "/mcp/servers/{serverId}/resources",
|
|
196
|
+
summary: "List Server Resources",
|
|
197
|
+
description: "Retrieves all resources available from a specific MCP server",
|
|
198
|
+
tags: ["mcp"],
|
|
199
|
+
request: {
|
|
200
|
+
params: import_zod_openapi.z.object({ serverId: import_zod_openapi.z.string().describe("The ID of the MCP server") })
|
|
201
|
+
},
|
|
202
|
+
responses: {
|
|
203
|
+
200: {
|
|
204
|
+
description: "Server resources",
|
|
205
|
+
content: { "application/json": { schema: ResourcesListResponseSchema } }
|
|
206
|
+
},
|
|
207
|
+
404: { description: "Not found" }
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
const getResourceContentRoute = (0, import_zod_openapi.createRoute)({
|
|
211
|
+
method: "get",
|
|
212
|
+
path: "/mcp/servers/{serverId}/resources/{resourceId}/content",
|
|
213
|
+
summary: "Read Server Resource Content",
|
|
214
|
+
description: "Reads content from a specific resource on an MCP server. This endpoint automatically constructs the qualified URI format (mcp:serverId:resourceId)",
|
|
215
|
+
tags: ["mcp"],
|
|
216
|
+
request: {
|
|
217
|
+
params: import_zod_openapi.z.object({
|
|
218
|
+
serverId: import_zod_openapi.z.string().describe("The ID of the MCP server"),
|
|
219
|
+
resourceId: import_zod_openapi.z.string().min(1, "Resource ID is required").transform((encoded) => decodeURIComponent(encoded)).describe("The URI-encoded resource identifier on that server")
|
|
220
|
+
})
|
|
221
|
+
},
|
|
222
|
+
responses: {
|
|
223
|
+
200: {
|
|
224
|
+
description: "Resource content",
|
|
225
|
+
content: { "application/json": { schema: ResourceContentResponseSchema } }
|
|
226
|
+
},
|
|
227
|
+
404: { description: "Not found" }
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
return app.openapi(addServerRoute, async (ctx) => {
|
|
231
|
+
const agent = getAgent();
|
|
232
|
+
const { name, config, persistToAgent } = ctx.req.valid("json");
|
|
233
|
+
await agent.connectMcpServer(name, config);
|
|
234
|
+
import_core.logger.info(`Successfully connected to new server '${name}' via API request.`);
|
|
235
|
+
if (persistToAgent === true) {
|
|
236
|
+
try {
|
|
237
|
+
const currentConfig = agent.getEffectiveConfig();
|
|
238
|
+
const updates = {
|
|
239
|
+
mcpServers: {
|
|
240
|
+
...currentConfig.mcpServers || {},
|
|
241
|
+
[name]: config
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const newConfig = await (0, import_agent_management.updateAgentConfigFile)(
|
|
245
|
+
agent.getAgentFilePath(),
|
|
246
|
+
updates
|
|
247
|
+
);
|
|
248
|
+
const reloadResult = await agent.reload(newConfig);
|
|
249
|
+
if (reloadResult.restarted) {
|
|
250
|
+
import_core.logger.info(
|
|
251
|
+
`Agent restarted to apply changes: ${reloadResult.changesApplied.join(", ")}`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
import_core.logger.info(`Saved server '${name}' to agent configuration file`);
|
|
255
|
+
} catch (saveError) {
|
|
256
|
+
const errorMessage = saveError instanceof Error ? saveError.message : String(saveError);
|
|
257
|
+
import_core.logger.warn(
|
|
258
|
+
`Failed to save server '${name}' to agent config: ${errorMessage}`,
|
|
259
|
+
{
|
|
260
|
+
error: saveError
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return ctx.json({ status: "connected", name }, 200);
|
|
266
|
+
}).openapi(listServersRoute, async (ctx) => {
|
|
267
|
+
const agent = getAgent();
|
|
268
|
+
const clientsMap = agent.getMcpClients();
|
|
269
|
+
const failedConnections = agent.getMcpFailedConnections();
|
|
270
|
+
const servers = [];
|
|
271
|
+
for (const name of clientsMap.keys()) {
|
|
272
|
+
servers.push({ id: name, name, status: "connected" });
|
|
273
|
+
}
|
|
274
|
+
for (const name of Object.keys(failedConnections)) {
|
|
275
|
+
servers.push({ id: name, name, status: "error" });
|
|
276
|
+
}
|
|
277
|
+
return ctx.json({ servers });
|
|
278
|
+
}).openapi(toolsRoute, async (ctx) => {
|
|
279
|
+
const agent = getAgent();
|
|
280
|
+
const { serverId } = ctx.req.valid("param");
|
|
281
|
+
const client = agent.getMcpClients().get(serverId);
|
|
282
|
+
if (!client) {
|
|
283
|
+
return ctx.json({ error: `Server '${serverId}' not found` }, 404);
|
|
284
|
+
}
|
|
285
|
+
const toolsMap = await client.getTools();
|
|
286
|
+
const tools = Object.entries(toolsMap).map(([toolName, toolDef]) => ({
|
|
287
|
+
id: toolName,
|
|
288
|
+
name: toolName,
|
|
289
|
+
description: toolDef.description || "",
|
|
290
|
+
inputSchema: toolDef.parameters
|
|
291
|
+
}));
|
|
292
|
+
return ctx.json({ tools });
|
|
293
|
+
}).openapi(deleteServerRoute, async (ctx) => {
|
|
294
|
+
const agent = getAgent();
|
|
295
|
+
const { serverId } = ctx.req.valid("param");
|
|
296
|
+
const clientExists = agent.getMcpClients().has(serverId) || agent.getMcpFailedConnections()[serverId];
|
|
297
|
+
if (!clientExists) {
|
|
298
|
+
return ctx.json({ error: `Server '${serverId}' not found.` }, 404);
|
|
299
|
+
}
|
|
300
|
+
await agent.removeMcpServer(serverId);
|
|
301
|
+
return ctx.json({ status: "disconnected", id: serverId });
|
|
302
|
+
}).openapi(restartServerRoute, async (ctx) => {
|
|
303
|
+
const agent = getAgent();
|
|
304
|
+
const { serverId } = ctx.req.valid("param");
|
|
305
|
+
import_core.logger.info(`Received request to POST /api/mcp/servers/${serverId}/restart`);
|
|
306
|
+
const clientExists = agent.getMcpClients().has(serverId);
|
|
307
|
+
if (!clientExists) {
|
|
308
|
+
import_core.logger.warn(`Attempted to restart non-existent server: ${serverId}`);
|
|
309
|
+
return ctx.json({ error: `Server '${serverId}' not found.` }, 404);
|
|
310
|
+
}
|
|
311
|
+
await agent.restartMcpServer(serverId);
|
|
312
|
+
return ctx.json({ status: "restarted", id: serverId });
|
|
313
|
+
}).openapi(execToolRoute, async (ctx) => {
|
|
314
|
+
const agent = getAgent();
|
|
315
|
+
const { serverId, toolName } = ctx.req.valid("param");
|
|
316
|
+
const body = ctx.req.valid("json");
|
|
317
|
+
const client = agent.getMcpClients().get(serverId);
|
|
318
|
+
if (!client) {
|
|
319
|
+
return ctx.json({ success: false, error: `Server '${serverId}' not found` }, 404);
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const rawResult = await client.callTool(toolName, body);
|
|
323
|
+
return ctx.json({ success: true, data: rawResult });
|
|
324
|
+
} catch (error) {
|
|
325
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
326
|
+
import_core.logger.error(
|
|
327
|
+
`Tool execution failed for '${toolName}' on server '${serverId}': ${errorMessage}`,
|
|
328
|
+
{ error }
|
|
329
|
+
);
|
|
330
|
+
return ctx.json({ success: false, error: errorMessage }, 200);
|
|
331
|
+
}
|
|
332
|
+
}).openapi(listResourcesRoute, async (ctx) => {
|
|
333
|
+
const agent = getAgent();
|
|
334
|
+
const { serverId } = ctx.req.valid("param");
|
|
335
|
+
const client = agent.getMcpClients().get(serverId);
|
|
336
|
+
if (!client) {
|
|
337
|
+
return ctx.json({ error: `Server '${serverId}' not found` }, 404);
|
|
338
|
+
}
|
|
339
|
+
const resources = await agent.listResourcesForServer(serverId);
|
|
340
|
+
return ctx.json({ success: true, resources });
|
|
341
|
+
}).openapi(getResourceContentRoute, async (ctx) => {
|
|
342
|
+
const agent = getAgent();
|
|
343
|
+
const { serverId, resourceId } = ctx.req.valid("param");
|
|
344
|
+
const client = agent.getMcpClients().get(serverId);
|
|
345
|
+
if (!client) {
|
|
346
|
+
return ctx.json({ error: `Server '${serverId}' not found` }, 404);
|
|
347
|
+
}
|
|
348
|
+
const qualifiedUri = `mcp:${serverId}:${resourceId}`;
|
|
349
|
+
const content = await agent.readResource(qualifiedUri);
|
|
350
|
+
return ctx.json({ success: true, data: { content } });
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
354
|
+
0 && (module.exports = {
|
|
355
|
+
createMcpRouter
|
|
356
|
+
});
|