@opengeni/api-router 0.12.0 → 0.12.2
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/dist/app.js +1 -1
- package/dist/{chunk-S2N4252E.js → chunk-3JR6L6NJ.js} +74 -32
- package/dist/chunk-3JR6L6NJ.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +10 -10
- package/src/app.ts +7 -3
- package/src/mcp/server.ts +20 -0
- package/src/mcp/toolspace.ts +44 -3
- package/dist/chunk-S2N4252E.js.map +0 -1
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/api-router",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -44,17 +44,17 @@
|
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
45
|
"@opengeni/agent-proto": "^0.3.0",
|
|
46
46
|
"@opengeni/codex": "^0.2.7",
|
|
47
|
-
"@opengeni/config": "^0.7.
|
|
48
|
-
"@opengeni/contracts": "^0.20.
|
|
49
|
-
"@opengeni/core": "^0.12.
|
|
50
|
-
"@opengeni/db": "^0.13.
|
|
51
|
-
"@opengeni/documents": "^0.2.
|
|
52
|
-
"@opengeni/events": "^0.3.
|
|
53
|
-
"@opengeni/github": "^0.3.
|
|
47
|
+
"@opengeni/config": "^0.7.8",
|
|
48
|
+
"@opengeni/contracts": "^0.20.1",
|
|
49
|
+
"@opengeni/core": "^0.12.2",
|
|
50
|
+
"@opengeni/db": "^0.13.1",
|
|
51
|
+
"@opengeni/documents": "^0.2.38",
|
|
52
|
+
"@opengeni/events": "^0.3.29",
|
|
53
|
+
"@opengeni/github": "^0.3.20",
|
|
54
54
|
"@opengeni/network": "^0.1.1",
|
|
55
55
|
"@opengeni/observability": "^0.3.0",
|
|
56
|
-
"@opengeni/runtime": "^0.13.
|
|
57
|
-
"@opengeni/storage": "^0.2.
|
|
56
|
+
"@opengeni/runtime": "^0.13.13",
|
|
57
|
+
"@opengeni/storage": "^0.2.32",
|
|
58
58
|
"@temporalio/client": "^1.17.0",
|
|
59
59
|
"better-auth": "^1.6.14",
|
|
60
60
|
"hono": "^4.12.18",
|
package/src/app.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
configuredAllowedModels,
|
|
4
4
|
configuredAllowedReasoningEfforts,
|
|
5
5
|
configuredModels,
|
|
6
|
+
withCodexCatalogProvider,
|
|
6
7
|
} from "@opengeni/config";
|
|
7
8
|
import {
|
|
8
9
|
ClientConfig,
|
|
@@ -322,18 +323,21 @@ export function createApp(deps: AppDependencies): Hono {
|
|
|
322
323
|
|
|
323
324
|
app.get("/v1/config/client", (c) => {
|
|
324
325
|
c.header("cache-control", "no-store");
|
|
326
|
+
const catalogSettings = deps.settings.codexSubscriptionEnabled
|
|
327
|
+
? withCodexCatalogProvider(deps.settings)
|
|
328
|
+
: deps.settings;
|
|
325
329
|
return c.json(
|
|
326
330
|
ClientConfig.parse({
|
|
327
331
|
deploymentRevision: deps.settings.deploymentRevision,
|
|
328
332
|
apiContractRevision: OPENGENI_API_CONTRACT_REVISION,
|
|
329
333
|
...(deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {}),
|
|
330
|
-
defaultModel: canonicalizeConfiguredModelId(
|
|
331
|
-
allowedModels: configuredAllowedModels(
|
|
334
|
+
defaultModel: canonicalizeConfiguredModelId(catalogSettings, catalogSettings.openaiModel),
|
|
335
|
+
allowedModels: configuredAllowedModels(catalogSettings),
|
|
332
336
|
// Provider-grouped model list for the picker. configuredModels() carries the
|
|
333
337
|
// union of the built-in allow-list and every registry provider's models, in
|
|
334
338
|
// selection order (default model first); project each to the client-safe
|
|
335
339
|
// ClientModel shape (ConfiguredModel.providerId → ClientModel.provider).
|
|
336
|
-
models: configuredModels(
|
|
340
|
+
models: configuredModels(catalogSettings).map(projectClientModel),
|
|
337
341
|
defaultReasoningEffort: deps.settings.openaiReasoningEffort,
|
|
338
342
|
allowedReasoningEfforts: configuredAllowedReasoningEfforts(deps.settings),
|
|
339
343
|
mcpServers: deps.settings.mcpServers.map((server) => ({
|
package/src/mcp/server.ts
CHANGED
|
@@ -776,6 +776,26 @@ function registerToolspaceProxyTools(server: McpServer, surface: ToolspaceMcpSur
|
|
|
776
776
|
if (!surface) {
|
|
777
777
|
return;
|
|
778
778
|
}
|
|
779
|
+
// McpServer installs its tools/list handler lazily on the first registered
|
|
780
|
+
// tool. A legitimate empty Toolspace surface (no selected proxyable servers,
|
|
781
|
+
// no active turn, or all optional upstreams unavailable) must therefore seed
|
|
782
|
+
// and disable one invisible tool; otherwise `ogtool list` receives JSON-RPC
|
|
783
|
+
// "Method not found" instead of the valid `{ tools: [] }` response.
|
|
784
|
+
if (surface.tools.length === 0) {
|
|
785
|
+
server
|
|
786
|
+
.registerTool(
|
|
787
|
+
"__opengeni_empty_toolspace_surface__",
|
|
788
|
+
{
|
|
789
|
+
description: "Internal disabled placeholder for an empty Toolspace surface.",
|
|
790
|
+
inputSchema: z4.object({}),
|
|
791
|
+
},
|
|
792
|
+
async () => ({
|
|
793
|
+
content: [{ type: "text" as const, text: '{"unavailable":true}' }],
|
|
794
|
+
}),
|
|
795
|
+
)
|
|
796
|
+
.disable();
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
779
799
|
for (const tool of surface.tools) {
|
|
780
800
|
server.registerTool(
|
|
781
801
|
tool.name,
|
package/src/mcp/toolspace.ts
CHANGED
|
@@ -367,7 +367,13 @@ async function resolveToolListing(input: {
|
|
|
367
367
|
sessionId,
|
|
368
368
|
rootSessionId,
|
|
369
369
|
turn: activeTurn,
|
|
370
|
-
}).catch(() =>
|
|
370
|
+
}).catch((error) => {
|
|
371
|
+
deps.observability?.warn("toolspace upstream connection failed", {
|
|
372
|
+
serverId,
|
|
373
|
+
...toolspaceErrorAttributes(error),
|
|
374
|
+
});
|
|
375
|
+
return null;
|
|
376
|
+
});
|
|
371
377
|
if (!connection) {
|
|
372
378
|
aggregateBudget.replace(serverId, []);
|
|
373
379
|
return;
|
|
@@ -375,7 +381,13 @@ async function resolveToolListing(input: {
|
|
|
375
381
|
try {
|
|
376
382
|
const listed = await connection.client
|
|
377
383
|
.listTools(undefined, toolspaceRequestOptions(config))
|
|
378
|
-
.catch(() =>
|
|
384
|
+
.catch((error) => {
|
|
385
|
+
deps.observability?.warn("toolspace upstream tool list failed", {
|
|
386
|
+
serverId,
|
|
387
|
+
...toolspaceErrorAttributes(error),
|
|
388
|
+
});
|
|
389
|
+
return { tools: [] };
|
|
390
|
+
});
|
|
379
391
|
let boundedTools: readonly McpTool[];
|
|
380
392
|
try {
|
|
381
393
|
boundedTools = assertMcpToolListWithinBounds(listed.tools as McpTool[]) as McpTool[];
|
|
@@ -494,7 +506,18 @@ async function connectToolspaceServer(input: {
|
|
|
494
506
|
rootSessionId: string;
|
|
495
507
|
turn: SessionTurn;
|
|
496
508
|
}): Promise<ConnectedToolspaceServer> {
|
|
497
|
-
|
|
509
|
+
// npm Undici's dispatcher transport is not reliable under Bun. The worker's
|
|
510
|
+
// model-visible MCP path already uses Bun's native fetch while retaining the
|
|
511
|
+
// same pre-request destination-policy check; Toolspace must do the same or an
|
|
512
|
+
// embedded Bun API can expose a server to the model while silently dropping
|
|
513
|
+
// that exact server from `ogtool list`.
|
|
514
|
+
const useBunNativeFetch = !!process.versions.bun;
|
|
515
|
+
const mcpFetchImpl: FetchLike = useBunNativeFetch
|
|
516
|
+
? globalThis.fetch.bind(globalThis)
|
|
517
|
+
: undiciFetch;
|
|
518
|
+
const guardedFetch = guardedMcpFetch(input.deps.settings, mcpFetchImpl, {
|
|
519
|
+
...(useBunNativeFetch ? { pinResolvedDestination: false } : {}),
|
|
520
|
+
});
|
|
498
521
|
const baseFetch: FetchLike = input.config.connectionRef
|
|
499
522
|
? connectionBrokerFetch(guardedFetch, input)
|
|
500
523
|
: guardedFetch;
|
|
@@ -730,6 +753,24 @@ function toolspaceAuditSummary(value: unknown): {
|
|
|
730
753
|
};
|
|
731
754
|
}
|
|
732
755
|
|
|
756
|
+
function toolspaceErrorAttributes(error: unknown): {
|
|
757
|
+
errorClass: string;
|
|
758
|
+
errorCode?: string;
|
|
759
|
+
errorMessage?: string;
|
|
760
|
+
} {
|
|
761
|
+
if (!(error instanceof Error)) {
|
|
762
|
+
return { errorClass: typeof error };
|
|
763
|
+
}
|
|
764
|
+
const code = (error as Error & { code?: unknown }).code;
|
|
765
|
+
return {
|
|
766
|
+
errorClass: error.name,
|
|
767
|
+
...(typeof code === "string" ? { errorCode: code } : {}),
|
|
768
|
+
...(error.message
|
|
769
|
+
? { errorMessage: error.message.replaceAll(/[\r\n]+/gu, " ").slice(0, 512) }
|
|
770
|
+
: {}),
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
|
|
733
774
|
type ToolspaceReservation =
|
|
734
775
|
| { status: "ok"; turn: SessionTurn }
|
|
735
776
|
| { status: "no_active_turn" }
|