@ggui-ai/mcp-server 0.27.0 → 0.29.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 +4 -2
- package/dist/build-mcp.d.ts +84 -1
- package/dist/build-mcp.d.ts.map +1 -1
- package/dist/build-mcp.js +306 -3
- package/dist/console-config-routes.js +1 -1
- package/dist/console-mcp-tools-routes.d.ts +1 -1
- package/dist/console-mcp-tools-routes.d.ts.map +1 -1
- package/dist/console-mcp-tools-routes.js +1 -1
- package/dist/control-service.d.ts +1 -1
- package/dist/control-service.d.ts.map +1 -1
- package/dist/control-service.js +1 -1
- package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
- package/dist/ggui-session-channel/subscribe.js +24 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -2
- package/dist/llm-backed-negotiator.d.ts +10 -48
- package/dist/llm-backed-negotiator.d.ts.map +1 -1
- package/dist/llm-backed-negotiator.js +5 -135
- package/dist/mcp-apps-outbound.d.ts.map +1 -1
- package/dist/mcp-apps-outbound.js +24 -3
- package/dist/mcp-endpoint-routes.d.ts.map +1 -1
- package/dist/mcp-endpoint-routes.js +27 -2
- package/dist/oauth-as-routes.d.ts +3 -0
- package/dist/oauth-as-routes.d.ts.map +1 -1
- package/dist/oauth-as-routes.js +20 -13
- package/dist/oauth.d.ts +24 -18
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +42 -40
- package/dist/request-context.d.ts +19 -0
- package/dist/request-context.d.ts.map +1 -1
- package/dist/request-context.js +17 -1
- package/dist/server.d.ts +33 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +44 -2
- package/dist/storage.d.ts +11 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4 -3
- package/package.json +16 -16
package/dist/index.d.ts
CHANGED
|
@@ -29,10 +29,12 @@ export type { HandlerContext, SharedHandler } from '@ggui-ai/mcp-server-handlers
|
|
|
29
29
|
export type { GadgetDescriptor, McpUiDisplayMode, GguiSession, SystemGguiSession, } from '@ggui-ai/protocol';
|
|
30
30
|
export type { GenerationDeps } from '@ggui-ai/mcp-server-handlers';
|
|
31
31
|
export { buildNoCredentialsGguiSession } from '@ggui-ai/mcp-server-handlers';
|
|
32
|
-
export { buildOpsBundleHandlers, createGguiServer, defaultHandlers, DEFAULT_CLOSE_GRACE_MS } from './server.js';
|
|
32
|
+
export { buildOpsBundleHandlers, createGguiServer, defaultHandlers, DEFAULT_CLOSE_GRACE_MS, DEFAULT_HEADERS_TIMEOUT_MS, DEFAULT_KEEP_ALIVE_TIMEOUT_MS, } from './server.js';
|
|
33
33
|
export type { CreateGguiServerOptions, ErrorMapperResult, GguiServer, OpsBundleDeps, } from './server.js';
|
|
34
|
-
export {
|
|
35
|
-
export type {
|
|
34
|
+
export { buildLlmCaller } from './llm-backed-negotiator.js';
|
|
35
|
+
export type { BuildLlmCallerOptions } from './llm-backed-negotiator.js';
|
|
36
|
+
export { AnthropicStructuredCallError, isAnthropicStructuredCallError, } from '@ggui-ai/ui-gen/providers';
|
|
37
|
+
export type { AnthropicStructuredCallFailureKind } from '@ggui-ai/ui-gen/providers';
|
|
36
38
|
export { buildControlService, CONTROL_PATH, filterHandlersByAudience, SINGLE_CALL_OPS, } from './control-service.js';
|
|
37
39
|
export type { AudienceTag, BuildControlServiceArgs } from './control-service.js';
|
|
38
40
|
export { computeRuntimeBundleHash, insertRuntimeBundleHash, resolveHashedRuntimeBundleUrl, resolveRuntimeBundleHash, } from './runtime-bundle-hash.js';
|
|
@@ -50,6 +52,7 @@ export type { ServerInfo } from './build-mcp.js';
|
|
|
50
52
|
export { UnauthenticatedError, DEFAULT_BUILDER_APP_ID, defaultAppIdFromIdentity, } from './auth.js';
|
|
51
53
|
export { createConsoleLogger } from './logger.js';
|
|
52
54
|
export type { Logger } from './logger.js';
|
|
55
|
+
export { currentRequestUserAgent } from './request-context.js';
|
|
53
56
|
export { ChannelSessionNotFoundError, createGguiSessionChannelServer, DEFAULT_RENDER_CHANNEL_PATH, } from './ggui-session-channel.js';
|
|
54
57
|
export type { GguiSessionChannelOptions, GguiSessionChannelServer, SubscriberSink, } from './ggui-session-channel.js';
|
|
55
58
|
export { resolveStorageFromConfig } from './storage.js';
|
|
@@ -83,6 +86,8 @@ export type { GoogleLoginProviderOptions } from './oauth-providers/google.js';
|
|
|
83
86
|
export { githubLoginProvider } from './oauth-providers/github.js';
|
|
84
87
|
export type { GithubLoginProviderOptions } from './oauth-providers/github.js';
|
|
85
88
|
export { createOAuthProvidersStore } from './oauth-providers-store.js';
|
|
89
|
+
export type { OAuthConfig, OAuthStorage, AuthCodeRecord, ClientRecord } from './oauth.js';
|
|
90
|
+
export { InMemoryOAuthStorage } from './oauth.js';
|
|
86
91
|
export type { OAuthProvidersStore, OAuthProvidersStoreOptions, PutInput as OAuthProvidersStorePutInput, } from './oauth-providers-store.js';
|
|
87
92
|
export { DEFAULT_ADMIN_OAUTH_PROVIDERS_PATH, mountAdminOAuthProvidersTransport, } from './admin-oauth-providers-transport.js';
|
|
88
93
|
export type { AdminOAuthProvidersTransportOptions } from './admin-oauth-providers-transport.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAKlF,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -30,12 +30,17 @@
|
|
|
30
30
|
// `/settings` URL) without taking a direct `@ggui-ai/mcp-server-handlers`
|
|
31
31
|
// dependency.
|
|
32
32
|
export { buildNoCredentialsGguiSession } from '@ggui-ai/mcp-server-handlers';
|
|
33
|
-
export { buildOpsBundleHandlers, createGguiServer, defaultHandlers, DEFAULT_CLOSE_GRACE_MS } from './server.js';
|
|
33
|
+
export { buildOpsBundleHandlers, createGguiServer, defaultHandlers, DEFAULT_CLOSE_GRACE_MS, DEFAULT_HEADERS_TIMEOUT_MS, DEFAULT_KEEP_ALIVE_TIMEOUT_MS, } from './server.js';
|
|
34
34
|
// The handshake negotiator's LLM caller (the rerank judge + the contract
|
|
35
35
|
// synthesizer). Exported so a deployment that builds its own negotiator
|
|
36
36
|
// reuses this one Anthropic structured-call implementation — and can bound
|
|
37
37
|
// it with an abort signal — instead of carrying a copy.
|
|
38
|
-
|
|
38
|
+
// The named error is the shared structured call's
|
|
39
|
+
// (`@ggui-ai/ui-gen/providers`), re-exported with its guard. Match on
|
|
40
|
+
// `isAnthropicStructuredCallError`, which holds even when a second copy
|
|
41
|
+
// of `@ggui-ai/ui-gen` is installed; `instanceof` does not.
|
|
42
|
+
export { buildLlmCaller } from './llm-backed-negotiator.js';
|
|
43
|
+
export { AnthropicStructuredCallError, isAnthropicStructuredCallError, } from '@ggui-ai/ui-gen/providers';
|
|
39
44
|
// Control plane (`/control`) — the composition that projects every
|
|
40
45
|
// `protocol`- and `ops`-tagged handler onto one anonymous-capable
|
|
41
46
|
// route with per-tool auth + confirmation gates. `createGguiServer`
|
|
@@ -73,6 +78,9 @@ export { composePreviewReservedValidator, mergeReservedValidators, } from './res
|
|
|
73
78
|
export { checkRenderSchemaCompat, DEFAULT_SCHEMA_COMPAT_MODE, hasErrorFinding, SchemaCompatError, } from './schema-compat.js';
|
|
74
79
|
export { UnauthenticatedError, DEFAULT_BUILDER_APP_ID, defaultAppIdFromIdentity, } from './auth.js';
|
|
75
80
|
export { createConsoleLogger } from './logger.js';
|
|
81
|
+
// The client-set User-Agent of the request in progress, for attribution
|
|
82
|
+
// only (e.g. marking a log line); never an auth, trust or routing input.
|
|
83
|
+
export { currentRequestUserAgent } from './request-context.js';
|
|
76
84
|
export { ChannelSessionNotFoundError, createGguiSessionChannelServer, DEFAULT_RENDER_CHANNEL_PATH, } from './ggui-session-channel.js';
|
|
77
85
|
export { resolveStorageFromConfig } from './storage.js';
|
|
78
86
|
export { DEFAULT_PAIRING_ADMIN_INIT_PATH, DEFAULT_PAIRING_PATH, mountPairingTransport, } from './pairing-transport.js';
|
|
@@ -107,6 +115,7 @@ export { MCP_INSTRUCTIONS_PRESETS, resolveMcpInstructions, } from './instruction
|
|
|
107
115
|
export { googleLoginProvider } from './oauth-providers/google.js';
|
|
108
116
|
export { githubLoginProvider } from './oauth-providers/github.js';
|
|
109
117
|
export { createOAuthProvidersStore } from './oauth-providers-store.js';
|
|
118
|
+
export { InMemoryOAuthStorage } from './oauth.js';
|
|
110
119
|
export { DEFAULT_ADMIN_OAUTH_PROVIDERS_PATH, mountAdminOAuthProvidersTransport, } from './admin-oauth-providers-transport.js';
|
|
111
120
|
export { DEFAULT_BUILDER_OWNER_ID, DEFAULT_THREADS_PATH, defaultThreadOwnerFromIdentity, mountThreadTransport, } from './thread-transport.js';
|
|
112
121
|
// Zero-config in-memory reference for the console shortCode → render
|
|
@@ -42,18 +42,16 @@ import type { LLMCaller } from "@ggui-ai/negotiator";
|
|
|
42
42
|
*
|
|
43
43
|
* `callStructured` (and `callStructuredMetered`, the same call with the
|
|
44
44
|
* response's token usage beside the tool input — ggui#1418) is wired for
|
|
45
|
-
* Anthropic only
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* onto `ProviderAdapter` as an optional method and wire each
|
|
56
|
-
* adapter; this in-place implementation stays the bridge until then.
|
|
45
|
+
* Anthropic only, through the shared structured call
|
|
46
|
+
* (`anthropicToolCall` in `@ggui-ai/ui-gen/providers`, ggui#1271): one
|
|
47
|
+
* `/v1/messages` request asking for one tool call, a forced
|
|
48
|
+
* `tool_choice` where the model accepts one, and every way the turn ends
|
|
49
|
+
* without the tool input thrown as a NAMED `AnthropicStructuredCallError`
|
|
50
|
+
* (re-exported from this package's index). The rerank judge and the synthesizer collapse that
|
|
51
|
+
* throw to their null-decision fallback, with the kind in the reason.
|
|
52
|
+
* Other providers (OpenAI, Google, OpenRouter, Bedrock) omit
|
|
53
|
+
* `callStructured`; consumers detect absence and fall back to
|
|
54
|
+
* regex-JSON extraction on the text path.
|
|
57
55
|
*
|
|
58
56
|
* Used by:
|
|
59
57
|
* - `@ggui-ai/negotiator/llm-rerank` (Tier 2 RAG match judge)
|
|
@@ -76,42 +74,6 @@ export interface BuildLlmCallerOptions {
|
|
|
76
74
|
readonly signal?: AbortSignal;
|
|
77
75
|
}
|
|
78
76
|
export declare function buildLlmCaller(selection: LlmSelection, providerKey: ProviderKeyRef, options?: BuildLlmCallerOptions): LLMCaller;
|
|
79
|
-
/**
|
|
80
|
-
* Thinking budget added to the caller's ANSWER budget when the model
|
|
81
|
-
* refuses a forced tool. Those are the always-thinking models: their
|
|
82
|
-
* thinking counts against `max_tokens` and comes before the tool call,
|
|
83
|
-
* so a caller's small answer budget (the rerank judge asks for 512)
|
|
84
|
-
* would be spent before the `tool_use` block exists. `max_tokens` is a
|
|
85
|
-
* ceiling, not spend.
|
|
86
|
-
*/
|
|
87
|
-
export declare const ALWAYS_THINKING_HEADROOM_TOKENS = 16000;
|
|
88
|
-
/** How a structured call ended without the tool input it asked for. */
|
|
89
|
-
export type AnthropicStructuredCallFailureKind =
|
|
90
|
-
/** The budget ran out (`stop_reason: max_tokens`) before the tool call. */
|
|
91
|
-
"max_tokens"
|
|
92
|
-
/** The model finished without calling the tool (possible under `auto`). */
|
|
93
|
-
| "no_tool_call"
|
|
94
|
-
/** The model declined (`stop_reason: refusal`). */
|
|
95
|
-
| "refusal"
|
|
96
|
-
/** The API answered non-2xx. */
|
|
97
|
-
| "http"
|
|
98
|
-
/** The body was not a Messages response. */
|
|
99
|
-
| "malformed";
|
|
100
|
-
/**
|
|
101
|
-
* A structured call that returned no tool input, NAMED by why. The kind
|
|
102
|
-
* is in the message too (`[max_tokens]` …), so a consumer that only
|
|
103
|
-
* surfaces the message — the rerank judge's `reason` — still reports
|
|
104
|
-
* which failure it was instead of an anonymous null decision.
|
|
105
|
-
*/
|
|
106
|
-
export declare class AnthropicStructuredCallError extends Error {
|
|
107
|
-
readonly kind: AnthropicStructuredCallFailureKind;
|
|
108
|
-
readonly status: number | undefined;
|
|
109
|
-
readonly stopReason: string | undefined;
|
|
110
|
-
constructor(kind: AnthropicStructuredCallFailureKind, detail: string, extra?: {
|
|
111
|
-
readonly status?: number;
|
|
112
|
-
readonly stopReason?: string;
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
77
|
/**
|
|
116
78
|
* Dependencies for `createLlmBackedHandshakeNegotiator`. Only
|
|
117
79
|
* `resolveLlm` is required; the rest carry default fallbacks.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-backed-negotiator.d.ts","sourceRoot":"","sources":["../src/llm-backed-negotiator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAEjB,YAAY,EACZ,cAAc,EACd,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"llm-backed-negotiator.d.ts","sourceRoot":"","sources":["../src/llm-backed-negotiator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAEjB,YAAY,EACZ,cAAc,EACd,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,qBAAqB,CAAC;AAG9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,YAAY,EACvB,WAAW,EAAE,cAAc,EAC3B,OAAO,GAAE,qBAA0B,GAClC,SAAS,CAsDX;AAED;;;GAGG;AACH,MAAM,WAAW,gCAAgC;IAC/C;;;;;;OAMG;IACH,UAAU,EAAE,CACV,GAAG,EAAE,cAAc,KAEjB;QAAE,SAAS,EAAE,YAAY,CAAC;QAAC,WAAW,EAAE,cAAc,CAAA;KAAE,GACxD,OAAO,CAAC;QAAE,SAAS,EAAE,YAAY,CAAC;QAAC,WAAW,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAAC,GACxE,IAAI,CAAC;IACT;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE;QACN,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;QACtC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;QAClC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;KAChC,CAAC;IACF;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,2BAA2B,CAAC;IAClD;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,CAAC,gCAAgC,EAAE,OAAO,GAAG,qBAAqB,GAAG,WAAW,CAAC,GAC1F,aAAa,EAAE,CAQjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,gCAAgC,GACrC,mBAAmB,CAkCrB"}
|
|
@@ -31,8 +31,7 @@
|
|
|
31
31
|
* `resolveLlm` is wired into `generation`.
|
|
32
32
|
*/
|
|
33
33
|
import { decideHandshake, } from "@ggui-ai/mcp-server-handlers/renders";
|
|
34
|
-
import {
|
|
35
|
-
import { selectAdapter } from "@ggui-ai/ui-gen/providers";
|
|
34
|
+
import { anthropicToolCall, selectAdapter } from "@ggui-ai/ui-gen/providers";
|
|
36
35
|
export function buildLlmCaller(selection, providerKey, options = {}) {
|
|
37
36
|
const adapter = selectAdapter(selection.provider);
|
|
38
37
|
const isAnthropic = selection.provider === "anthropic";
|
|
@@ -65,149 +64,20 @@ export function buildLlmCaller(selection, providerKey, options = {}) {
|
|
|
65
64
|
},
|
|
66
65
|
};
|
|
67
66
|
if (isAnthropic) {
|
|
68
|
-
const metered = (systemPrompt, userMessage, tool, maxTokens) =>
|
|
67
|
+
const metered = (systemPrompt, userMessage, tool, maxTokens) => anthropicToolCall({
|
|
69
68
|
apiKey: providerKey.key,
|
|
70
69
|
model: selection.model,
|
|
71
70
|
systemPrompt,
|
|
72
|
-
userMessage,
|
|
71
|
+
userPrompt: userMessage,
|
|
73
72
|
tool,
|
|
74
|
-
maxTokens,
|
|
73
|
+
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
75
74
|
...(options.signal !== undefined ? { signal: options.signal } : {}),
|
|
76
|
-
});
|
|
75
|
+
}).then(({ input, usage }) => ({ value: input, ...(usage !== undefined ? { usage } : {}) }));
|
|
77
76
|
caller.callStructuredMetered = metered;
|
|
78
77
|
caller.callStructured = async (systemPrompt, userMessage, tool, maxTokens) => (await metered(systemPrompt, userMessage, tool, maxTokens)).value;
|
|
79
78
|
}
|
|
80
79
|
return caller;
|
|
81
80
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Thinking budget added to the caller's ANSWER budget when the model
|
|
84
|
-
* refuses a forced tool. Those are the always-thinking models: their
|
|
85
|
-
* thinking counts against `max_tokens` and comes before the tool call,
|
|
86
|
-
* so a caller's small answer budget (the rerank judge asks for 512)
|
|
87
|
-
* would be spent before the `tool_use` block exists. `max_tokens` is a
|
|
88
|
-
* ceiling, not spend.
|
|
89
|
-
*/
|
|
90
|
-
export const ALWAYS_THINKING_HEADROOM_TOKENS = 16_000;
|
|
91
|
-
/**
|
|
92
|
-
* A structured call that returned no tool input, NAMED by why. The kind
|
|
93
|
-
* is in the message too (`[max_tokens]` …), so a consumer that only
|
|
94
|
-
* surfaces the message — the rerank judge's `reason` — still reports
|
|
95
|
-
* which failure it was instead of an anonymous null decision.
|
|
96
|
-
*/
|
|
97
|
-
export class AnthropicStructuredCallError extends Error {
|
|
98
|
-
kind;
|
|
99
|
-
status;
|
|
100
|
-
stopReason;
|
|
101
|
-
constructor(kind, detail, extra = {}) {
|
|
102
|
-
super(`anthropic structured call [${kind}]: ${detail}`);
|
|
103
|
-
this.name = "AnthropicStructuredCallError";
|
|
104
|
-
this.kind = kind;
|
|
105
|
-
this.status = extra.status;
|
|
106
|
-
this.stopReason = extra.stopReason;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Anthropic-direct tool-use call; returns the tool's `input` JSON.
|
|
111
|
-
*
|
|
112
|
-
* Two request shapes, chosen by the shared model-rule predicate
|
|
113
|
-
* (`anthropicRejectsForcedToolChoice`, the same list the harness router
|
|
114
|
-
* reads — one list, so a model the API starts refusing is added once):
|
|
115
|
-
*
|
|
116
|
-
* - Models that accept a forced tool: `tool_choice: {type: 'tool'}`
|
|
117
|
-
* and the caller's `max_tokens`, as before.
|
|
118
|
-
* - Models that refuse it (the always-thinking family): a forced tool
|
|
119
|
-
* is an HTTP 400 on every call, so they get `tool_choice: auto` with
|
|
120
|
-
* at most one call, an instruction naming the tool appended to the
|
|
121
|
-
* system prompt, and `ALWAYS_THINKING_HEADROOM_TOKENS` added to the
|
|
122
|
-
* caller's budget. `strict: true` is deliberately NOT sent: the two
|
|
123
|
-
* consumers' schemas carry keywords strict tool use rejects (the
|
|
124
|
-
* rerank judge's `minimum`/`maximum`, the synthesizer's map-shaped
|
|
125
|
-
* `additionalProperties`), so it would trade one 400 for another;
|
|
126
|
-
* both consumers already validate the input they get back.
|
|
127
|
-
*
|
|
128
|
-
* Every way the turn ends without the tool input throws an
|
|
129
|
-
* `AnthropicStructuredCallError` whose kind says which — the caller
|
|
130
|
-
* (rerank judge, synthesizer) collapses it to its null-decision
|
|
131
|
-
* fallback, and the kind rides along in the reason.
|
|
132
|
-
*/
|
|
133
|
-
async function anthropicCallStructured(args) {
|
|
134
|
-
const answerBudget = args.maxTokens ?? 1024;
|
|
135
|
-
const refusesForcedTool = anthropicRejectsForcedToolChoice(args.model);
|
|
136
|
-
const body = {
|
|
137
|
-
model: args.model,
|
|
138
|
-
max_tokens: refusesForcedTool ? answerBudget + ALWAYS_THINKING_HEADROOM_TOKENS : answerBudget,
|
|
139
|
-
// `temperature` was pinned to 0 for deterministic structured
|
|
140
|
-
// output, but Anthropic deprecated the parameter on newer
|
|
141
|
-
// tool-use models (Haiku 4.5+ rejects it with HTTP 400). Dropped.
|
|
142
|
-
// Residual stochasticity is in field VALUES (e.g. action names);
|
|
143
|
-
// both consumers (synthesizer + rerank judge) MUST tolerate
|
|
144
|
-
// paraphrase via canonical-key normalisation rather than relying
|
|
145
|
-
// on temperature=0.
|
|
146
|
-
system: refusesForcedTool
|
|
147
|
-
? `${args.systemPrompt}\n\nAnswer by calling the \`${args.tool.name}\` tool exactly once. Do not answer in text.`
|
|
148
|
-
: args.systemPrompt,
|
|
149
|
-
messages: [{ role: "user", content: args.userMessage }],
|
|
150
|
-
tools: [
|
|
151
|
-
{
|
|
152
|
-
name: args.tool.name,
|
|
153
|
-
description: args.tool.description,
|
|
154
|
-
input_schema: args.tool.input_schema,
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
// Forced tool use where the model allows it — the model MUST emit
|
|
158
|
-
// exactly this tool. Where it doesn't, `auto` + one call + the
|
|
159
|
-
// instruction above, and a missing call is a named failure below.
|
|
160
|
-
tool_choice: refusesForcedTool
|
|
161
|
-
? { type: "auto", disable_parallel_tool_use: true }
|
|
162
|
-
: { type: "tool", name: args.tool.name },
|
|
163
|
-
};
|
|
164
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
165
|
-
method: "POST",
|
|
166
|
-
headers: {
|
|
167
|
-
"x-api-key": args.apiKey,
|
|
168
|
-
"anthropic-version": "2023-06-01",
|
|
169
|
-
"content-type": "application/json",
|
|
170
|
-
},
|
|
171
|
-
body: JSON.stringify(body),
|
|
172
|
-
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
173
|
-
});
|
|
174
|
-
if (!response.ok) {
|
|
175
|
-
const text = await response.text().catch(() => "");
|
|
176
|
-
throw new AnthropicStructuredCallError("http", `HTTP ${response.status}: ${text.slice(0, 500)}`, {
|
|
177
|
-
status: response.status,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
const json = await response.json();
|
|
181
|
-
if (!isRecord(json) || !Array.isArray(json["content"])) {
|
|
182
|
-
throw new AnthropicStructuredCallError("malformed", "response body is not a Messages response");
|
|
183
|
-
}
|
|
184
|
-
const stopReason = typeof json["stop_reason"] === "string" ? json["stop_reason"] : undefined;
|
|
185
|
-
const toolUse = json["content"].find((block) => isRecord(block) && block["type"] === "tool_use" && block["name"] === args.tool.name);
|
|
186
|
-
if (toolUse !== undefined && toolUse["input"] !== undefined) {
|
|
187
|
-
const usage = readMessagesUsage(json["usage"]);
|
|
188
|
-
return { value: toolUse["input"], ...(usage !== undefined ? { usage } : {}) };
|
|
189
|
-
}
|
|
190
|
-
if (stopReason === "max_tokens") {
|
|
191
|
-
throw new AnthropicStructuredCallError("max_tokens", `stop_reason=max_tokens before the "${args.tool.name}" tool_use block (max_tokens=${body.max_tokens})`, { stopReason });
|
|
192
|
-
}
|
|
193
|
-
if (stopReason === "refusal") {
|
|
194
|
-
throw new AnthropicStructuredCallError("refusal", `the model declined (stop_reason=refusal)`, { stopReason });
|
|
195
|
-
}
|
|
196
|
-
throw new AnthropicStructuredCallError("no_tool_call", `the turn ended (stop_reason=${stopReason ?? "absent"}) without a "${args.tool.name}" tool_use block`, stopReason !== undefined ? { stopReason } : {});
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* The token usage of a Messages response, as `{ input, output }`: its
|
|
200
|
-
* `usage.input_tokens` and `usage.output_tokens` when both are numbers,
|
|
201
|
-
* else absent (unmetered, never zeros — ggui#1418). No `cache_control` is
|
|
202
|
-
* sent on this call, so the cache token counts are not added.
|
|
203
|
-
*/
|
|
204
|
-
function readMessagesUsage(usage) {
|
|
205
|
-
if (!isRecord(usage))
|
|
206
|
-
return undefined;
|
|
207
|
-
const input = usage["input_tokens"];
|
|
208
|
-
const output = usage["output_tokens"];
|
|
209
|
-
return typeof input === "number" && typeof output === "number" ? { input, output } : undefined;
|
|
210
|
-
}
|
|
211
81
|
/**
|
|
212
82
|
* Build the handshake pool list: the per-app pool (from `cache`) first —
|
|
213
83
|
* so a deployment's own blueprints win on exact-key first-match — then
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAEhB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAWL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACpB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAmBjF,OAAO,EAgBL,KAAK,sBAAsB,EAE5B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAG3F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-apps-outbound.d.ts","sourceRoot":"","sources":["../src/mcp-apps-outbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -325,7 +325,10 @@ function readMetaFromCallToolResult(params){
|
|
|
325
325
|
return meta;
|
|
326
326
|
}
|
|
327
327
|
window.addEventListener('message',function(ev){
|
|
328
|
-
|
|
328
|
+
// Only the embedding window speaks for the host: a reply or a
|
|
329
|
+
// tool-result from any other frame is dropped, unanswered.
|
|
330
|
+
if(!ev||ev.source!==window.parent)return;
|
|
331
|
+
var m=ev.data;
|
|
329
332
|
if(!m||m.jsonrpc!=='2.0')return;
|
|
330
333
|
if(m.id!=null&&pending[m.id]){
|
|
331
334
|
var p=pending[m.id];delete pending[m.id];
|
|
@@ -556,7 +559,9 @@ const GGUI_INLINE_SHELL_BUFFER_SCRIPT_BODY = `
|
|
|
556
559
|
(function(){'use strict';
|
|
557
560
|
var buf=window.__GGUI_PENDING_TOOL_RESULTS__=window.__GGUI_PENDING_TOOL_RESULTS__||[];
|
|
558
561
|
window.addEventListener('message',function(ev){
|
|
559
|
-
|
|
562
|
+
// Only the embedding window's tool-results are buffered.
|
|
563
|
+
if(!ev||ev.source!==window.parent)return;
|
|
564
|
+
var m=ev.data;
|
|
560
565
|
if(!m||m.jsonrpc!=='2.0'||m.method!=='ui/notifications/tool-result')return;
|
|
561
566
|
buf.push(m.params);
|
|
562
567
|
if(buf.length>8)buf.splice(0,buf.length-8);
|
|
@@ -1520,6 +1525,11 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1520
1525
|
* would for a locator that never existed. The refusal is observable
|
|
1521
1526
|
* only server-side, on the audit line below — whose `rowAppId` names
|
|
1522
1527
|
* the render's owner whichever store answered for it.
|
|
1528
|
+
*
|
|
1529
|
+
* Every allowed read logs `render_resource_read` (ggui#1553: door, app,
|
|
1530
|
+
* auth source, and whether app trust admitted the caller over a
|
|
1531
|
+
* subject-bound row; never the session or the subject), so the rung-4
|
|
1532
|
+
* admission line has a denominator an operator can read beside it.
|
|
1523
1533
|
*/
|
|
1524
1534
|
function readAllowed(sessionId, candidate) {
|
|
1525
1535
|
const callerCtx = opts.getContext?.();
|
|
@@ -1532,8 +1542,19 @@ export function registerGguiRenderResourceTemplate(server, opts) {
|
|
|
1532
1542
|
: callerCtx;
|
|
1533
1543
|
const verdict = renderReadVerdict(candidate, fallbackCtx);
|
|
1534
1544
|
if (verdict.allowed) {
|
|
1545
|
+
// ggui#1553: one line per ALLOWED read — door, app, source, and
|
|
1546
|
+
// whether rung 4 (app trust) admitted the caller over a subject-bound
|
|
1547
|
+
// row — so the admission count below has the read traffic beside it.
|
|
1548
|
+
// Never the session or the subject.
|
|
1549
|
+
opts.logger?.info("render_resource_read", {
|
|
1550
|
+
door: "resources_read",
|
|
1551
|
+
appId: candidate.appId,
|
|
1552
|
+
source: fallbackCtx?.authSource ?? null,
|
|
1553
|
+
appTrustOverSubject: verdict.appTrustOverSubject === true,
|
|
1554
|
+
});
|
|
1535
1555
|
// ggui#1553: rung 4 admitted an app credential to a subject-bound
|
|
1536
|
-
// session. Door, app, source; never the session or the subject.
|
|
1556
|
+
// session. Door, app, source; never the session or the subject. Kept
|
|
1557
|
+
// as its own line: it is the one an operator counts.
|
|
1537
1558
|
if (verdict.appTrustOverSubject) {
|
|
1538
1559
|
opts.logger?.info(RENDER_READ_APP_TRUST_OVER_SUBJECT, {
|
|
1539
1560
|
door: "resources_read",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-endpoint-routes.d.ts","sourceRoot":"","sources":["../src/mcp-endpoint-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElF,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC,OAAO,
|
|
1
|
+
{"version":3,"file":"mcp-endpoint-routes.d.ts","sourceRoot":"","sources":["../src/mcp-endpoint-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ import { GGUI_HOST_CAPABILITIES_HEADER, MCP_ERROR_CODES, isRecord, parseHostCapa
|
|
|
35
35
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
36
36
|
import { randomUUID } from "node:crypto";
|
|
37
37
|
import { resolveIdentity, UnauthenticatedError } from "./auth.js";
|
|
38
|
-
import { buildMcpServer } from "./build-mcp.js";
|
|
38
|
+
import { buildMcpServer, observeToolCallRefusals } from "./build-mcp.js";
|
|
39
39
|
import { CONTROL_PATH, DATA_PLANE_AUDIENCES, filterHandlersByAudience, } from "./control-service.js";
|
|
40
40
|
import { buildWwwAuthenticate, resolveIssuerUrl } from "./oauth.js";
|
|
41
41
|
/** The JSON-RPC error object a mapped result becomes on the wire. */
|
|
@@ -374,13 +374,18 @@ export function mountMcpEndpoints(opts) {
|
|
|
374
374
|
});
|
|
375
375
|
const transport = new StreamableHTTPServerTransport({
|
|
376
376
|
sessionIdGenerator: undefined,
|
|
377
|
+
// #1828 — see `answersAsJson`.
|
|
378
|
+
enableJsonResponse: answersAsJson(req.body),
|
|
377
379
|
});
|
|
378
380
|
res.on("close", () => {
|
|
379
381
|
transport.close().catch(() => undefined);
|
|
380
382
|
mcp.close().catch(() => undefined);
|
|
381
383
|
});
|
|
382
384
|
try {
|
|
383
|
-
|
|
385
|
+
// ggui#1995 — the server connects through the refusal observer, so a
|
|
386
|
+
// `tools/call` the SDK refuses before any handler (element ceiling,
|
|
387
|
+
// input schema, unknown tool) still logs its `tool_invoked` line.
|
|
388
|
+
await mcp.connect(observeToolCallRefusals(transport, mcp, reqLogger, () => als.getStore() ?? ctx));
|
|
384
389
|
await als.run(ctx, () => transport.handleRequest(req, res, req.body));
|
|
385
390
|
}
|
|
386
391
|
catch (err) {
|
|
@@ -538,3 +543,23 @@ export function mountMcpEndpoints(opts) {
|
|
|
538
543
|
app.get(universalMcpPath, methodNotAllowed);
|
|
539
544
|
app.delete(universalMcpPath, methodNotAllowed);
|
|
540
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* The methods whose POST the route answers as `application/json` rather than
|
|
548
|
+
* as an SSE stream (#1828). Streamable HTTP lets a server pick either for any
|
|
549
|
+
* request, and a client must accept both.
|
|
550
|
+
*
|
|
551
|
+
* A resource read has nothing to stream: the answer is one message, and as
|
|
552
|
+
* JSON a proxy in front of the server can compress it, which matters for a
|
|
553
|
+
* ~1 MB `ui://` shell on a long path. Every other method keeps the stream: a
|
|
554
|
+
* tool call's headers go out at once, so a long call never waits on a
|
|
555
|
+
* time-to-first-byte limit somewhere between the host and the server. In JSON
|
|
556
|
+
* mode the headers would wait for the result.
|
|
557
|
+
*/
|
|
558
|
+
const JSON_ANSWERED_METHODS = new Set(["resources/read"]);
|
|
559
|
+
/** Whether this POST body is a single JSON-RPC request for a {@link JSON_ANSWERED_METHODS} method. */
|
|
560
|
+
function answersAsJson(body) {
|
|
561
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
562
|
+
return false;
|
|
563
|
+
const method = body.method;
|
|
564
|
+
return typeof method === "string" && JSON_ANSWERED_METHODS.has(method);
|
|
565
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { AuthAdapter, PairingService } from "@ggui-ai/mcp-server-core";
|
|
20
20
|
import type { Express } from "express";
|
|
21
21
|
import { type OAuthConfig, type OAuthStorage } from "./oauth.js";
|
|
22
|
+
import type { Logger } from "./logger.js";
|
|
22
23
|
interface MountOptions {
|
|
23
24
|
/** Express app to mount onto. */
|
|
24
25
|
readonly app: Express;
|
|
@@ -58,6 +59,8 @@ interface MountOptions {
|
|
|
58
59
|
* `listen()`, which is strictly after composition completes.
|
|
59
60
|
*/
|
|
60
61
|
readonly getPairingService: () => PairingService | null;
|
|
62
|
+
/** Where a failed OAuth route logs its error (the response never carries it). */
|
|
63
|
+
readonly logger: Logger;
|
|
61
64
|
}
|
|
62
65
|
/**
|
|
63
66
|
* Mount the OAuth discovery + auth + token endpoints onto the express
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-as-routes.d.ts","sourceRoot":"","sources":["../src/oauth-as-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"oauth-as-routes.d.ts","sourceRoot":"","sources":["../src/oauth-as-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAE1D,OAAO,EAQL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IACpC,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE;QACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IACF;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;IACxD,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CA+I5E"}
|
package/dist/oauth-as-routes.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* clients (Claude Desktop, claude.ai, Goose, etc.) bail with
|
|
17
17
|
* "couldn't reach".
|
|
18
18
|
*/
|
|
19
|
-
import { handleAuthorizationServerMetadata, handleAuthorizeGet, handleAuthorizePost, handleProtectedResourceMetadata, handleRegister, handleToken, } from "./oauth.js";
|
|
19
|
+
import { handleAuthorizationServerMetadata, handleAuthorizeGet, handleAuthorizePost, handleProtectedResourceMetadata, handleRegister, handleToken, renderErrorPage, } from "./oauth.js";
|
|
20
20
|
/**
|
|
21
21
|
* Mount the OAuth discovery + auth + token endpoints onto the express
|
|
22
22
|
* app. Also flips `trust proxy` on — nginx-ingress + ALB both
|
|
@@ -111,16 +111,23 @@ export function mountOAuthAuthorizationServerRoutes(opts) {
|
|
|
111
111
|
app.get(`/.well-known/oauth-authorization-server${universalMcpPath}`, (req, res) => handleAuthorizationServerMetadata(req, res, oauthConfig));
|
|
112
112
|
}
|
|
113
113
|
app.get("/.well-known/oauth-authorization-server", (req, res) => handleAuthorizationServerMetadata(req, res, oauthConfig));
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
114
|
+
// A handler that throws (a storage that fails) answers a defined error,
|
|
115
|
+
// never a hung request, and never the error itself: the browser routes get
|
|
116
|
+
// the generic error page, the JSON routes RFC 6749 / 7591's `server_error`,
|
|
117
|
+
// and the error goes to the server log.
|
|
118
|
+
const failClosed = (route, shape, handler) => (req, res) => {
|
|
119
|
+
handler(req, res).catch((err) => {
|
|
120
|
+
opts.logger.error("oauth_route_failed", { route, error: err instanceof Error ? err.message : String(err) });
|
|
121
|
+
if (res.headersSent)
|
|
122
|
+
return;
|
|
123
|
+
if (shape === "html")
|
|
124
|
+
res.status(500).type("html").send(renderErrorPage("Something went wrong on the server. Please try again."));
|
|
125
|
+
else
|
|
126
|
+
res.status(500).json({ error: "server_error", error_description: "The authorization server could not complete the request." });
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
app.post("/oauth/register", failClosed("register", "json", (req, res) => handleRegister(req, res, oauthConfig, oauthStorage)));
|
|
130
|
+
app.get("/oauth/authorize", failClosed("authorize", "html", (req, res) => handleAuthorizeGet(req, res, oauthConfig, oauthStorage)));
|
|
131
|
+
app.post("/oauth/authorize", failClosed("authorize", "html", (req, res) => handleAuthorizePost(req, res, oauthConfig, oauthStorage, auth, getPairingService())));
|
|
132
|
+
app.post("/oauth/token", failClosed("token", "json", (req, res) => handleToken(req, res, oauthStorage)));
|
|
126
133
|
}
|