@roarkanalytics/sdk-mcp 4.9.0 → 4.11.1
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/CHANGELOG.md +21 -0
- package/README.md +58 -0
- package/code-tool.js +10 -0
- package/code-tool.js.map +1 -1
- package/code-tool.mjs +10 -0
- package/code-tool.mjs.map +1 -1
- package/http.d.mts.map +1 -1
- package/http.d.ts.map +1 -1
- package/http.js +95 -2
- package/http.js.map +1 -1
- package/http.mjs +95 -2
- package/http.mjs.map +1 -1
- package/local-docs-search.js +6 -6
- package/local-docs-search.js.map +1 -1
- package/local-docs-search.mjs +6 -6
- package/local-docs-search.mjs.map +1 -1
- package/oauth.d.mts +70 -0
- package/oauth.d.mts.map +1 -0
- package/oauth.d.ts +70 -0
- package/oauth.d.ts.map +1 -0
- package/oauth.js +101 -0
- package/oauth.js.map +1 -0
- package/oauth.mjs +93 -0
- package/oauth.mjs.map +1 -0
- package/options.d.mts +8 -0
- package/options.d.mts.map +1 -1
- package/options.d.ts +8 -0
- package/options.d.ts.map +1 -1
- package/options.js +26 -0
- package/options.js.map +1 -1
- package/options.mjs +26 -0
- package/options.mjs.map +1 -1
- package/package.json +12 -2
- package/server.d.mts.map +1 -1
- package/server.d.ts.map +1 -1
- package/server.js +2 -1
- package/server.js.map +1 -1
- package/server.mjs +2 -1
- package/server.mjs.map +1 -1
- package/src/code-tool.ts +10 -0
- package/src/http.ts +111 -3
- package/src/local-docs-search.ts +6 -6
- package/src/oauth.ts +137 -0
- package/src/options.ts +39 -0
- package/src/server.ts +2 -1
- package/src/types.ts +10 -0
- package/types.d.mts +10 -0
- package/types.d.mts.map +1 -1
- package/types.d.ts +10 -0
- package/types.d.ts.map +1 -1
- package/types.js.map +1 -1
- package/types.mjs.map +1 -1
package/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAGtF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,wBAAoB;AACvC,OAAO,cAAc,+BAA2B;AAChD,OAAO,EAAE,cAAc,EAAE,+BAA2B;AACpD,OAAO,EAAE,eAAe,EAAE,gCAA4B;AACtD,OAAO,EAAE,eAAe,EAAE,2BAAuB;AAEjD,OAAO,EAAE,yBAAyB,EAAE,sBAAkB;AAGtD,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,EACjC,eAAe,EACf,sBAAsB,GAIvB,EAAE,EAAE,CACH,IAAI,SAAS,CACX;IACE,IAAI,EAAE,wBAAwB;IAC9B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.mjs","sourceRoot":"","sources":["src/server.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAGtF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,wBAAoB;AACvC,OAAO,cAAc,+BAA2B;AAChD,OAAO,EAAE,cAAc,EAAE,+BAA2B;AACpD,OAAO,EAAE,eAAe,EAAE,gCAA4B;AACtD,OAAO,EAAE,eAAe,EAAE,2BAAuB;AAEjD,OAAO,EAAE,yBAAyB,EAAE,sBAAkB;AAGtD,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,EACjC,eAAe,EACf,sBAAsB,GAIvB,EAAE,EAAE,CACH,IAAI,SAAS,CACX;IACE,IAAI,EAAE,wBAAwB;IAC9B,OAAO,EAAE,QAAQ,EAAE,2BAA2B;CAC/C,EACD;IACE,YAAY,EAAE,MAAM,eAAe,CAAC,EAAE,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAChF,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;CACzC,CACF,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAoC,CAAC;AAEtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAA4B,EAA4B,EAAE;IAC3F,MAAM,GAAG,GAAG,OAAO,IAAI,EAAE,CAAC;IAC1B,IAAI,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACjF,iBAAiB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QACH,iBAAiB,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAQnC;IACC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,YAAY,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IAEzF,MAAM,UAAU,GACd,CAAC,KAA6C,EAAE,EAAE,CAClD,CAAC,OAAe,EAAE,GAAG,IAAe,EAAE,EAAE;QACtC,KAAK,MAAM,CAAC,kBAAkB,CAAC;YAC7B,KAAK;YACL,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;SACxB,CAAC,CAAC;IACL,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;QAC1B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC;QACxB,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC;QAC3B,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;KAC3B,CAAC;IAEF,IAAI,MAAM,CAAC,UAAU,EAAE,cAAc,KAAK,OAAO,EAAE,CAAC;QAClD,cAAc,CAAC,MAAM,kBAAkB,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,OAA0B,CAAC;IAC/B,IAAI,YAA+B,CAAC;IACpC,IAAI,SAAkE,CAAC;IAEvE,MAAM,SAAS,GAAG,GAAU,EAAE;QAC5B,IAAI,YAAY;YAAE,MAAM,YAAY,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,KAAK,CAAC;oBAClB,MAAM;oBACN,GAAG,MAAM,CAAC,aAAa;oBACvB,cAAc,EAAE;wBACd,GAAG,MAAM,CAAC,aAAa,EAAE,cAAc;wBACvC,iBAAiB,EAAE,MAAM;qBAC1B;iBACF,CAAC,CAAC;gBACH,IAAI,SAAS,EAAE,CAAC;oBACd,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,YAAY,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7D,MAAM,YAAY,CAAC;YACrB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjG,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAC1D,OAAO;YACL,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;SACpD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,MAAa,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,GAAG,SAAS,EAAE,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBAC/F;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,OAAO,cAAc,CAAC;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,UAAU,EAAE;gBACV,MAAM;gBACN,OAAO,EAAE,MAAM,CAAC,aAAa,EAAE,OAAO,IAAI,SAAS;gBACnD,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,UAAU,EAAE,eAAe;gBAC7E,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,aAAa,EAAE,MAAM,CAAC,aAAa;aACpC;YACD,IAAI;SACL,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QACjC,IAAI,QAAqD,CAAC;QAC1D,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,QAAQ,GAAG,OAAO,CAAC;gBACnB,MAAM;YACR,KAAK,MAAM;gBACT,QAAQ,GAAG,MAAM,CAAC;gBAClB,MAAM;YACR,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS;gBACZ,QAAQ,GAAG,MAAM,CAAC;gBAClB,MAAM;YACR,KAAK,OAAO;gBACV,QAAQ,GAAG,OAAO,CAAC;gBACnB,MAAM;YACR;gBACE,QAAQ,GAAG,KAAK,CAAC;gBACjB,MAAM;QACV,CAAC;QACD,SAAS,GAAG,QAAQ,CAAC;QACrB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC9C,MAAM,aAAa,GAAG,EAAE,CAAC;IAEzB,IAAI,OAAO,EAAE,eAAe,IAAI,IAAI,EAAE,CAAC;QACrC,aAAa,CAAC,IAAI,CAChB,QAAQ,CAAC;YACP,cAAc,EAAE,yBAAyB,CAAC,OAAO,CAAC;YAClD,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,IAAI,mBAAmB;SACrE,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,EAAE,gBAAgB,IAAI,IAAI,EAAE,CAAC;QACtC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,OAAO,EACP,UAAU,EACV,IAAI,GAKL;IACC,OAAO,MAAM,OAAO,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC"}
|
package/src/code-tool.ts
CHANGED
|
@@ -321,9 +321,19 @@ const localDenoHandler = async ({
|
|
|
321
321
|
|
|
322
322
|
// Strip null/undefined values so that the worker SDK client can fall back to
|
|
323
323
|
// reading from environment variables (including any upstreamClientEnvs).
|
|
324
|
+
//
|
|
325
|
+
// This is an allow-list, not a spread of `client`: the worker runs in a separate Deno
|
|
326
|
+
// process and only what is named here crosses into it. `project` has to be named or a
|
|
327
|
+
// pinned connector silently loses its project, and every call from inside the sandbox
|
|
328
|
+
// fails with "this credential is not tied to a single project". Code the model writes can
|
|
329
|
+
// still override it per call with `client.withOptions({ project })`.
|
|
330
|
+
//
|
|
331
|
+
// It comes off `reqContext`, not the client: `client.project` is the `/v1/projects`
|
|
332
|
+
// resource accessor, so reading the option there returns a resource object.
|
|
324
333
|
const opts = {
|
|
325
334
|
...(client.baseURL != null ? { baseURL: client.baseURL } : undefined),
|
|
326
335
|
...(client.bearerToken != null ? { bearerToken: client.bearerToken } : undefined),
|
|
336
|
+
...(reqContext.project != null ? { project: reqContext.project } : undefined),
|
|
327
337
|
defaultHeaders: {
|
|
328
338
|
'X-Stainless-MCP': 'true',
|
|
329
339
|
},
|
package/src/http.ts
CHANGED
|
@@ -3,14 +3,34 @@
|
|
|
3
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
|
|
4
4
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
5
|
import { ClientOptions } from '@roarkanalytics/sdk';
|
|
6
|
+
import cors from 'cors';
|
|
6
7
|
import express from 'express';
|
|
7
8
|
import pino from 'pino';
|
|
8
9
|
import pinoHttp from 'pino-http';
|
|
9
10
|
import { getStainlessApiKey, parseClientAuthHeaders } from './auth';
|
|
10
11
|
import { getLogger } from './logger';
|
|
11
12
|
import { McpOptions } from './options';
|
|
13
|
+
import {
|
|
14
|
+
Connector,
|
|
15
|
+
ORIGIN_CONNECTOR,
|
|
16
|
+
protectedResourceMetadata,
|
|
17
|
+
requireBearer,
|
|
18
|
+
UnauthorizedError,
|
|
19
|
+
} from './oauth';
|
|
12
20
|
import { initMcpServer, newMcpServer } from './server';
|
|
13
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Which connector URL this request arrived on.
|
|
24
|
+
*
|
|
25
|
+
* `req.path` is the path within the matched route, so `/mcp` and `/mcp/<id>` are distinguishable
|
|
26
|
+
* from the legacy `/` transport without threading a flag through every handler.
|
|
27
|
+
*/
|
|
28
|
+
const connectorFrom = (req: express.Request): Connector => {
|
|
29
|
+
const projectId = req.params?.['projectId'];
|
|
30
|
+
if (typeof projectId === 'string' && projectId.length > 0) return { kind: 'project', projectId };
|
|
31
|
+
return req.path === '/mcp' || req.path.startsWith('/mcp/') ? { kind: 'mcp' } : ORIGIN_CONNECTOR;
|
|
32
|
+
};
|
|
33
|
+
|
|
14
34
|
const newServer = async ({
|
|
15
35
|
clientOptions,
|
|
16
36
|
mcpOptions,
|
|
@@ -22,12 +42,25 @@ const newServer = async ({
|
|
|
22
42
|
req: express.Request;
|
|
23
43
|
res: express.Response;
|
|
24
44
|
}): Promise<McpServer | null> => {
|
|
45
|
+
// Remote OAuth mode: a bearer is mandatory and a missing one is answered with
|
|
46
|
+
// 401 + WWW-Authenticate so the client discovers the authorization server. The
|
|
47
|
+
// token is a Roark API key and is forwarded to the SDK unchanged. Without oauth
|
|
48
|
+
// config we keep the legacy optional-bearer behavior (local installs).
|
|
49
|
+
//
|
|
50
|
+
// This runs before the server is built on purpose: `newMcpServer` fetches the
|
|
51
|
+
// instructions document over the network, and an unauthenticated caller should
|
|
52
|
+
// not be able to make us do that.
|
|
53
|
+
let authOptions: Partial<ClientOptions>;
|
|
54
|
+
if (mcpOptions.oauth) {
|
|
55
|
+
authOptions = requireBearer(req, mcpOptions.oauth, connectorFrom(req));
|
|
56
|
+
} else {
|
|
57
|
+
authOptions = parseClientAuthHeaders(req, false);
|
|
58
|
+
}
|
|
59
|
+
|
|
25
60
|
const stainlessApiKey = getStainlessApiKey(req, mcpOptions);
|
|
26
61
|
const customInstructionsPath = mcpOptions.customInstructionsPath;
|
|
27
62
|
const server = await newMcpServer({ stainlessApiKey, customInstructionsPath });
|
|
28
63
|
|
|
29
|
-
const authOptions = parseClientAuthHeaders(req, false);
|
|
30
|
-
|
|
31
64
|
let upstreamClientEnvs: Record<string, string> | undefined;
|
|
32
65
|
const clientEnvsHeader = req.headers['x-stainless-mcp-client-envs'];
|
|
33
66
|
if (typeof clientEnvsHeader === 'string') {
|
|
@@ -97,7 +130,21 @@ const newServer = async ({
|
|
|
97
130
|
const post =
|
|
98
131
|
(options: { clientOptions: ClientOptions; mcpOptions: McpOptions }) =>
|
|
99
132
|
async (req: express.Request, res: express.Response) => {
|
|
100
|
-
|
|
133
|
+
let server: McpServer | null;
|
|
134
|
+
try {
|
|
135
|
+
server = await newServer({ ...options, req, res });
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error instanceof UnauthorizedError) {
|
|
138
|
+
res.setHeader('WWW-Authenticate', error.wwwAuthenticate);
|
|
139
|
+
res.status(error.status).json({
|
|
140
|
+
jsonrpc: '2.0',
|
|
141
|
+
error: { code: -32001, message: error.message },
|
|
142
|
+
id: req.body?.id ?? null,
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
101
148
|
// If we return null, we already set the authorization error.
|
|
102
149
|
if (server === null) return;
|
|
103
150
|
const transport = new StreamableHTTPServerTransport();
|
|
@@ -145,6 +192,24 @@ export const streamableHTTPApp = ({
|
|
|
145
192
|
}): express.Express => {
|
|
146
193
|
const app = express();
|
|
147
194
|
app.set('query parser', 'extended');
|
|
195
|
+
// Browser-based clients (Claude web, ChatGPT web) need CORS on the MCP and
|
|
196
|
+
// metadata endpoints, and must be able to read the challenge header.
|
|
197
|
+
//
|
|
198
|
+
// `origin: true` reflects the caller's Origin, which CodeQL flags as permissive.
|
|
199
|
+
// It is deliberate: this is a public resource server whose only credential is
|
|
200
|
+
// the bearer a client attaches per request. No cookies are ever set and
|
|
201
|
+
// `credentials` stays off, so reflecting the origin is equivalent to `*` and
|
|
202
|
+
// grants a page nothing it could not already do with a token it holds. An
|
|
203
|
+
// allowlist would have to enumerate every MCP client's web origin (Claude,
|
|
204
|
+
// ChatGPT, Cursor, VS Code webviews, Inspector on localhost) and break the
|
|
205
|
+
// next one to appear.
|
|
206
|
+
app.use(
|
|
207
|
+
cors({
|
|
208
|
+
origin: true,
|
|
209
|
+
exposedHeaders: ['WWW-Authenticate', 'mcp-session-id'],
|
|
210
|
+
allowedHeaders: ['authorization', 'content-type', 'mcp-session-id'],
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
148
213
|
app.use(express.json());
|
|
149
214
|
app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
150
215
|
const existing = req.headers['mcp-session-id'];
|
|
@@ -197,10 +262,53 @@ export const streamableHTTPApp = ({
|
|
|
197
262
|
app.get('/health', async (req: express.Request, res: express.Response) => {
|
|
198
263
|
res.status(200).send('OK');
|
|
199
264
|
});
|
|
265
|
+
|
|
266
|
+
// RFC 9728 Protected Resource Metadata, the discovery entrypoint every OAuth
|
|
267
|
+
// MCP client hits first. Served per resource: the server origin, and each
|
|
268
|
+
// project-scoped connector URL (its `resource` is what the client echoes to
|
|
269
|
+
// the authorization server, which is how consent learns the project). Only
|
|
270
|
+
// served in remote OAuth mode.
|
|
271
|
+
if (mcpOptions.oauth) {
|
|
272
|
+
const oauth = mcpOptions.oauth;
|
|
273
|
+
app.get('/.well-known/oauth-protected-resource', (_req, res) => {
|
|
274
|
+
res.status(200).json(protectedResourceMetadata(oauth));
|
|
275
|
+
});
|
|
276
|
+
// The single connector. Its `resource` carries no project, which is what tells the
|
|
277
|
+
// authorization server to mint a credential for the person rather than for one project.
|
|
278
|
+
app.get('/.well-known/oauth-protected-resource/mcp', (_req, res) => {
|
|
279
|
+
res.status(200).json(protectedResourceMetadata(oauth, { kind: 'mcp' }));
|
|
280
|
+
});
|
|
281
|
+
app.get('/.well-known/oauth-protected-resource/mcp/:projectId', (req, res) => {
|
|
282
|
+
const projectId = req.params['projectId'];
|
|
283
|
+
res
|
|
284
|
+
.status(200)
|
|
285
|
+
.json(
|
|
286
|
+
protectedResourceMetadata(
|
|
287
|
+
oauth,
|
|
288
|
+
typeof projectId === 'string' ? { kind: 'project', projectId } : undefined,
|
|
289
|
+
),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
200
294
|
app.get('/', get);
|
|
201
295
|
app.post('/', post({ clientOptions, mcpOptions }));
|
|
202
296
|
app.delete('/', del);
|
|
203
297
|
|
|
298
|
+
// The connector URL: <base>/mcp, with no project in it. One URL, added once, that reaches
|
|
299
|
+
// every project the person belongs to. Registered BEFORE the pinned route so express does not
|
|
300
|
+
// match a bare `/mcp` as `/mcp/:projectId` with an empty parameter.
|
|
301
|
+
app.get('/mcp', get);
|
|
302
|
+
app.post('/mcp', post({ clientOptions, mcpOptions }));
|
|
303
|
+
app.delete('/mcp', del);
|
|
304
|
+
|
|
305
|
+
// Pinned connector URL: /mcp/<projectId>. Still supported, and now enforced rather than
|
|
306
|
+
// decorative: the project in the path becomes the SDK's project, so a user-scoped credential
|
|
307
|
+
// used here acts on that project only.
|
|
308
|
+
app.get('/mcp/:projectId', get);
|
|
309
|
+
app.post('/mcp/:projectId', post({ clientOptions, mcpOptions }));
|
|
310
|
+
app.delete('/mcp/:projectId', del);
|
|
311
|
+
|
|
204
312
|
return app;
|
|
205
313
|
};
|
|
206
314
|
|
package/src/local-docs-search.ts
CHANGED
|
@@ -395,9 +395,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
|
|
|
395
395
|
qualified: 'client.metric.listDefinitions',
|
|
396
396
|
params: ['after?: string;', 'limit?: number;'],
|
|
397
397
|
response:
|
|
398
|
-
"{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PROVIDER'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }[]; pagination: { hasMore: boolean; limit: number; nextCursor: string; }; }",
|
|
398
|
+
"{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PROVIDER'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }[]; pagination: { hasMore: boolean; limit: number; nextCursor: string; }; }",
|
|
399
399
|
markdown:
|
|
400
|
-
"## listDefinitions\n\n`client.metric.listDefinitions(after?: string, limit?: number): { data: object[]; pagination: object; }`\n\n**get** `/v1/metric/definitions`\n\nFetch metric definitions available in the project, including both system-generated and custom metrics. Results are ordered by immutable definition ID; pass `nextCursor` to retrieve the following page.\n\n### Parameters\n\n- `after?: string`\n\n- `limit?: number`\n\n### Returns\n\n- `{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PROVIDER'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }[]; pagination: { hasMore: boolean; limit: number; nextCursor: string; }; }`\n Cursor-paginated metric definitions available in the project\n\n - `data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PROVIDER'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: { sourceMetricDefinitionId: string; sourceVariantId: string; }[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; windowAfter: number; windowBefore: number; }; triggerCombinator: 'AND' | 'OR'; triggers: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }[]`\n - `pagination: { hasMore: boolean; limit: number; nextCursor: string; }`\n\n### Example\n\n```typescript\nimport Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark();\n\nconst response = await client.metric.listDefinitions();\n\nconsole.log(response);\n```",
|
|
400
|
+
"## listDefinitions\n\n`client.metric.listDefinitions(after?: string, limit?: number): { data: object[]; pagination: object; }`\n\n**get** `/v1/metric/definitions`\n\nFetch metric definitions available in the project, including both system-generated and custom metrics. Results are ordered by immutable definition ID; pass `nextCursor` to retrieve the following page.\n\n### Parameters\n\n- `after?: string`\n\n- `limit?: number`\n\n### Returns\n\n- `{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PROVIDER'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }[]; pagination: { hasMore: boolean; limit: number; nextCursor: string; }; }`\n Cursor-paginated metric definitions available in the project\n\n - `data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PROVIDER'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: { sourceMetricDefinitionId: string; sourceVariantId: string; }[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; windowAfter: number; windowBefore: number; }; triggerCombinator: 'AND' | 'OR'; triggers: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }[]`\n - `pagination: { hasMore: boolean; limit: number; nextCursor: string; }`\n\n### Example\n\n```typescript\nimport Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark();\n\nconst response = await client.metric.listDefinitions();\n\nconsole.log(response);\n```",
|
|
401
401
|
perLanguage: {
|
|
402
402
|
typescript: {
|
|
403
403
|
method: 'client.metric.listDefinitions',
|
|
@@ -425,9 +425,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
|
|
|
425
425
|
stainlessPath: '(resource) metric > (method) createDefinition',
|
|
426
426
|
qualified: 'client.metric.createDefinition',
|
|
427
427
|
response:
|
|
428
|
-
"{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }; }",
|
|
428
|
+
"{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }; }",
|
|
429
429
|
markdown:
|
|
430
|
-
"## createDefinition\n\n`client.metric.createDefinition(): { data: object; }`\n\n**post** `/v1/metric/definitions`\n\nCreate a new metric definition. The `calculationType` field selects the variant: LLM_JUDGE (LLM-evaluated), FORMULA (computed from a math expression over other metrics), or PATTERN (detects a trigger→outcome pattern within a window). To create a threshold on top of an existing metric, use `POST /metric/definitions/{idOrSlug}/thresholds` instead.\n\n### Returns\n\n- `{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }; }`\n\n - `data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: { sourceMetricDefinitionId: string; sourceVariantId: string; }[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; windowAfter: number; windowBefore: number; }; triggerCombinator: 'AND' | 'OR'; triggers: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }`\n\n### Example\n\n```typescript\nimport Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark();\n\nconst response = await client.metric.createDefinition({\n calculationType: 'LLM_JUDGE',\n name: 'Customer Satisfaction',\n outputType: 'BOOLEAN',\n});\n\nconsole.log(response);\n```",
|
|
430
|
+
"## createDefinition\n\n`client.metric.createDefinition(): { data: object; }`\n\n**post** `/v1/metric/definitions`\n\nCreate a new metric definition. The `calculationType` field selects the variant: LLM_JUDGE (LLM-evaluated), FORMULA (computed from a math expression over other metrics), or PATTERN (detects a trigger→outcome pattern within a window). To create a threshold on top of an existing metric, use `POST /metric/definitions/{idOrSlug}/thresholds` instead.\n\n### Returns\n\n- `{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }; }`\n\n - `data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: { sourceMetricDefinitionId: string; sourceVariantId: string; }[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; windowAfter: number; windowBefore: number; }; triggerCombinator: 'AND' | 'OR'; triggers: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; }`\n\n### Example\n\n```typescript\nimport Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark();\n\nconst response = await client.metric.createDefinition({\n calculationType: 'LLM_JUDGE',\n name: 'Customer Satisfaction',\n outputType: 'BOOLEAN',\n});\n\nconsole.log(response);\n```",
|
|
431
431
|
perLanguage: {
|
|
432
432
|
typescript: {
|
|
433
433
|
method: 'client.metric.createDefinition',
|
|
@@ -483,9 +483,9 @@ const EMBEDDED_METHODS: MethodEntry[] = [
|
|
|
483
483
|
'toolDefinitionIds?: string[];',
|
|
484
484
|
],
|
|
485
485
|
response:
|
|
486
|
-
"{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; }; }",
|
|
486
|
+
"{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; }; }",
|
|
487
487
|
markdown:
|
|
488
|
-
"## updateDefinition\n\n`client.metric.updateDefinition(idOrSlug: string, analysisPackageId?: object, booleanFalseLabel?: string, booleanTrueLabel?: string, calcType?: object, changeReason?: string, classificationOptions?: { description: string; displayOrder: number; label: string; }[], formula?: string, llmPrompt?: string, maxClassifications?: number, metricId?: object, name?: string, organizationId?: object, outputType?: object, participantRole?: object, projectId?: object, scaleLabels?: { displayOrder: number; label: string; rangeMax: number; rangeMin: number; colorHex?: string; description?: string; }[], scaleMax?: number, scaleMin?: number, scope?: object, slug?: object, source?: object, sources?: { sourceMetricDefinitionId: string; sourceVariantId?: string; }[], supportedContexts?: 'CALL' | 'SEGMENT' | 'TURN'[], supportsMultipleVariants?: object, toolDefinitionIds?: string[]): { data: object; }`\n\n**put** `/v1/metric/definitions/{idOrSlug}`\n\nUpdate the editable subset of a custom metric definition, addressed by its UUID or its stable `slug`. Only the supplied fields are changed; omitted fields are left unchanged. Every update creates a new immutable version; the response carries the advanced `versionId`. Immutable fields (scope, outputType, calcType, …) are rejected, and which fields are editable depends on the metric (e.g. derived metrics only allow `name`). Roark's own metrics are rejected here: this endpoint edits the shared definition, which every workspace sees. To change one for your workspace alone, edit its variant with PUT /v1/metric/definitions/{idOrSlug}/variants/{variantId}, which forks it for you and leaves every other workspace on the original.\n\n### Parameters\n\n- `idOrSlug: string`\n\n- `analysisPackageId?: object`\n\n- `booleanFalseLabel?: string`\n New label for the false case (BOOLEAN output only)\n\n- `booleanTrueLabel?: string`\n New label for the true case (BOOLEAN output only)\n\n- `calcType?: object`\n\n- `changeReason?: string`\n Optional free-text audit note recorded on the new version.\n\n- `classificationOptions?: { description: string; displayOrder: number; label: string; }[]`\n Replacement set of classification options (CLASSIFICATION output only)\n\n- `formula?: string`\n New formula expression (FORMULA only). Pass `sources` alongside if the referenced metrics change.\n\n- `llmPrompt?: string`\n New LLM prompt (only for LLM_JUDGE metrics whose prompt is editable)\n\n- `maxClassifications?: number`\n New maximum number of classifications (CLASSIFICATION output only)\n\n- `metricId?: object`\n\n- `name?: string`\n New name (only for metrics whose name is editable)\n\n- `organizationId?: object`\n\n- `outputType?: object`\n\n- `participantRole?: object`\n\n- `projectId?: object`\n\n- `scaleLabels?: { displayOrder: number; label: string; rangeMax: number; rangeMin: number; colorHex?: string; description?: string; }[]`\n Replacement set of scale-range labels (SCALE output only)\n\n- `scaleMax?: number`\n New scale maximum (SCALE output only)\n\n- `scaleMin?: number`\n New scale minimum (SCALE output only)\n\n- `scope?: object`\n\n- `slug?: object`\n\n- `source?: object`\n\n- `sources?: { sourceMetricDefinitionId: string; sourceVariantId?: string; }[]`\n Replacement formula sources, required when `formula` changes the referenced metrics (FORMULA only).\n\n- `supportedContexts?: 'CALL' | 'SEGMENT' | 'TURN'[]`\n Replacement set of supported contexts. Omit to leave unchanged.\n\n- `supportsMultipleVariants?: object`\n\n- `toolDefinitionIds?: string[]`\n Replacement set of scoped tool-definition ids (only for metrics whose tool scoping is editable)\n\n### Returns\n\n- `{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; }; }`\n\n - `data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; formula: { expression: string; sources: { sourceMetricDefinitionId: string; sourceVariantId: string; }[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; windowAfter: number; windowBefore: number; }; triggerCombinator: 'AND' | 'OR'; triggers: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; }`\n\n### Example\n\n```typescript\nimport Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark();\n\nconst response = await client.metric.updateDefinition('idOrSlug');\n\nconsole.log(response);\n```",
|
|
488
|
+
"## updateDefinition\n\n`client.metric.updateDefinition(idOrSlug: string, analysisPackageId?: object, booleanFalseLabel?: string, booleanTrueLabel?: string, calcType?: object, changeReason?: string, classificationOptions?: { description: string; displayOrder: number; label: string; }[], formula?: string, llmPrompt?: string, maxClassifications?: number, metricId?: object, name?: string, organizationId?: object, outputType?: object, participantRole?: object, projectId?: object, scaleLabels?: { displayOrder: number; label: string; rangeMax: number; rangeMin: number; colorHex?: string; description?: string; }[], scaleMax?: number, scaleMin?: number, scope?: object, slug?: object, source?: object, sources?: { sourceMetricDefinitionId: string; sourceVariantId?: string; }[], supportedContexts?: 'CALL' | 'SEGMENT' | 'TURN'[], supportsMultipleVariants?: object, toolDefinitionIds?: string[]): { data: object; }`\n\n**put** `/v1/metric/definitions/{idOrSlug}`\n\nUpdate the editable subset of a custom metric definition, addressed by its UUID or its stable `slug`. Only the supplied fields are changed; omitted fields are left unchanged. Every update creates a new immutable version; the response carries the advanced `versionId`. Immutable fields (scope, outputType, calcType, …) are rejected, and which fields are editable depends on the metric (e.g. derived metrics only allow `name`). Roark's own metrics are rejected here: this endpoint edits the shared definition, which every workspace sees. To change one for your workspace alone, edit its variant with PUT /v1/metric/definitions/{idOrSlug}/variants/{variantId}, which forks it for you and leaves every other workspace on the original.\n\n### Parameters\n\n- `idOrSlug: string`\n\n- `analysisPackageId?: object`\n\n- `booleanFalseLabel?: string`\n New label for the false case (BOOLEAN output only)\n\n- `booleanTrueLabel?: string`\n New label for the true case (BOOLEAN output only)\n\n- `calcType?: object`\n\n- `changeReason?: string`\n Optional free-text audit note recorded on the new version.\n\n- `classificationOptions?: { description: string; displayOrder: number; label: string; }[]`\n Replacement set of classification options (CLASSIFICATION output only)\n\n- `formula?: string`\n New formula expression (FORMULA only). Pass `sources` alongside if the referenced metrics change.\n\n- `llmPrompt?: string`\n New LLM prompt (only for LLM_JUDGE metrics whose prompt is editable)\n\n- `maxClassifications?: number`\n New maximum number of classifications (CLASSIFICATION output only)\n\n- `metricId?: object`\n\n- `name?: string`\n New name (only for metrics whose name is editable)\n\n- `organizationId?: object`\n\n- `outputType?: object`\n\n- `participantRole?: object`\n\n- `projectId?: object`\n\n- `scaleLabels?: { displayOrder: number; label: string; rangeMax: number; rangeMin: number; colorHex?: string; description?: string; }[]`\n Replacement set of scale-range labels (SCALE output only)\n\n- `scaleMax?: number`\n New scale maximum (SCALE output only)\n\n- `scaleMin?: number`\n New scale minimum (SCALE output only)\n\n- `scope?: object`\n\n- `slug?: object`\n\n- `source?: object`\n\n- `sources?: { sourceMetricDefinitionId: string; sourceVariantId?: string; }[]`\n Replacement formula sources, required when `formula` changes the referenced metrics (FORMULA only).\n\n- `supportedContexts?: 'CALL' | 'SEGMENT' | 'TURN'[]`\n Replacement set of supported contexts. Omit to leave unchanged.\n\n- `supportsMultipleVariants?: object`\n\n- `toolDefinitionIds?: string[]`\n Replacement set of scoped tool-definition ids (only for metrics whose tool scoping is editable)\n\n### Returns\n\n- `{ data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: object[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: object; triggerCombinator: 'AND' | 'OR'; triggers: object[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; }; }`\n\n - `data: { id: string; booleanFalseLabel: string; booleanTrueLabel: string; calculationType: 'LLM_JUDGE'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; llmPrompt: string; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'FORMULA'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; formula: { expression: string; sources: { sourceMetricDefinitionId: string; sourceVariantId: string; }[]; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'PATTERN'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; pattern: { operation: 'PATTERN_EXISTS' | 'PATTERN_COUNT' | 'OUTCOME_AGGREGATE'; outcome: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; windowAfter: number; windowBefore: number; }; triggerCombinator: 'AND' | 'OR'; triggers: { operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }[]; windowMode: string; }; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; unit?: { name: string; symbol: string; }; } | { id: string; calculationType: 'THRESHOLD'; description: string; evidenceSource: { countsWhen: boolean; metricId: string; }; metricId: string; name: string; requiresLiveConversation: boolean; scope: 'GLOBAL' | 'PER_PARTICIPANT'; slug: string; supportedContexts: 'CALL' | 'SEGMENT' | 'TURN'[]; supportedConversationSources: 'SIMULATED' | 'LIVE'[]; supportsVariants: boolean; type: 'COUNT' | 'NUMERIC' | 'BOOLEAN' | 'SCALE' | 'TEXT' | 'CLASSIFICATION' | 'OFFSET'; variantId: string; versionId: string; threshold?: { aggregationMode: string; countThreshold: number; operator: 'GREATER_THAN' | 'GREATER_THAN_OR_EQUALS' | 'LESS_THAN' | 'LESS_THAN_OR_EQUALS' | 'EQUALS' | 'NOT_EQUALS'; sourceMetricDefinitionId: string; sourceParticipantRole: 'AGENT' | 'CUSTOMER' | 'SIMULATED_CUSTOMER' | 'BACKGROUND_SPEAKER'; sourceVariantId: string; thresholdValue: string; }; unit?: { name: string; symbol: string; }; }`\n\n### Example\n\n```typescript\nimport Roark from '@roarkanalytics/sdk';\n\nconst client = new Roark();\n\nconst response = await client.metric.updateDefinition('idOrSlug');\n\nconsole.log(response);\n```",
|
|
489
489
|
perLanguage: {
|
|
490
490
|
typescript: {
|
|
491
491
|
method: 'client.metric.updateDefinition',
|
package/src/oauth.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Remote OAuth resource-server support.
|
|
2
|
+
//
|
|
3
|
+
// This file is hand-maintained (not generated). It turns the HTTP transport into
|
|
4
|
+
// an MCP OAuth *resource server* (RFC 9728): it advertises Protected Resource
|
|
5
|
+
// Metadata pointing at Roark's authorization server (bern `mcp-oauth`) and
|
|
6
|
+
// answers requests without a bearer with 401 + WWW-Authenticate so clients know
|
|
7
|
+
// where to go. The access token the authorization server issues is a Roark API
|
|
8
|
+
// key (minted at consent with the permissions the user picked, expiring and
|
|
9
|
+
// rotated by the client's refresh token), so it is forwarded to customer-api as
|
|
10
|
+
// the SDK bearer unchanged: customer-api validates it on every call.
|
|
11
|
+
|
|
12
|
+
import { IncomingMessage } from 'node:http';
|
|
13
|
+
import { ClientOptions } from '@roarkanalytics/sdk';
|
|
14
|
+
|
|
15
|
+
export type OAuthConfig = {
|
|
16
|
+
/** Authorization server issuer, e.g. https://mcp-oauth.api.roark.ai */
|
|
17
|
+
issuer: string;
|
|
18
|
+
/** Public origin of this resource server, e.g. https://mcp.api.roark.ai; connector URLs are `<base>/mcp/<projectId>`. */
|
|
19
|
+
resourceBaseUrl: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Thrown when a request cannot be authorized; carries the WWW-Authenticate value to return. */
|
|
23
|
+
export class UnauthorizedError extends Error {
|
|
24
|
+
constructor(
|
|
25
|
+
message: string,
|
|
26
|
+
readonly wwwAuthenticate: string,
|
|
27
|
+
readonly status: 401 | 403 = 401,
|
|
28
|
+
) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'UnauthorizedError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const stripSlash = (url: string): string => url.replace(/\/+$/, '');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Which connector URL a request arrived on. There are three, and the difference is the whole
|
|
38
|
+
* point of this PR:
|
|
39
|
+
*
|
|
40
|
+
* - `origin` the bare server origin, the legacy `/` transport.
|
|
41
|
+
* - `mcp` the single connector, `<base>/mcp`. No project in the URL: the credential is
|
|
42
|
+
* user-scoped and names a project per request. This is what a person adds to
|
|
43
|
+
* Claude or Cursor once and never revisits.
|
|
44
|
+
* - `project` a pinned connector, `<base>/mcp/<projectId>`. Still supported, and now actually
|
|
45
|
+
* enforced: the project in the path becomes the SDK's project, so a user-scoped
|
|
46
|
+
* credential used on a pinned URL acts only on that project.
|
|
47
|
+
*/
|
|
48
|
+
export type Connector = { kind: 'origin' } | { kind: 'mcp' } | { kind: 'project'; projectId: string };
|
|
49
|
+
|
|
50
|
+
export const ORIGIN_CONNECTOR: Connector = { kind: 'origin' };
|
|
51
|
+
|
|
52
|
+
/** The path a connector is served on, relative to the origin. */
|
|
53
|
+
const connectorPath = (connector: Connector): string =>
|
|
54
|
+
connector.kind === 'origin' ? ''
|
|
55
|
+
: connector.kind === 'mcp' ? '/mcp'
|
|
56
|
+
: `/mcp/${connector.projectId}`;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The resource identifier (RFC 8707) for a request: the connector URL it arrived on. Clients
|
|
60
|
+
* send it back to the authorization server as `resource`, which is how consent learns whether
|
|
61
|
+
* the grant is for one project or for the person.
|
|
62
|
+
*/
|
|
63
|
+
export const resourceIdentifier = (config: OAuthConfig, connector: Connector = ORIGIN_CONNECTOR): string =>
|
|
64
|
+
`${stripSlash(config.resourceBaseUrl)}${connectorPath(connector)}`;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Where this server publishes its Protected Resource Metadata, per resource.
|
|
68
|
+
*
|
|
69
|
+
* RFC 9728 puts the resource's path after the well-known segment, so this is literally the
|
|
70
|
+
* origin, the well-known path, and the connector path, in that order.
|
|
71
|
+
*/
|
|
72
|
+
export const protectedResourceMetadataUrl = (
|
|
73
|
+
config: OAuthConfig,
|
|
74
|
+
connector: Connector = ORIGIN_CONNECTOR,
|
|
75
|
+
): string =>
|
|
76
|
+
`${stripSlash(config.resourceBaseUrl)}/.well-known/oauth-protected-resource${connectorPath(connector)}`;
|
|
77
|
+
|
|
78
|
+
/** RFC 9728 Protected Resource Metadata document. */
|
|
79
|
+
export const protectedResourceMetadata = (config: OAuthConfig, connector: Connector = ORIGIN_CONNECTOR) => ({
|
|
80
|
+
resource: resourceIdentifier(config, connector),
|
|
81
|
+
authorization_servers: [stripSlash(config.issuer)],
|
|
82
|
+
bearer_methods_supported: ['header'],
|
|
83
|
+
scopes_supported: ['mcp'],
|
|
84
|
+
resource_documentation: 'https://docs.roark.ai',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const bearerChallenge = (
|
|
88
|
+
config: OAuthConfig,
|
|
89
|
+
connector: Connector,
|
|
90
|
+
error?: string,
|
|
91
|
+
description?: string,
|
|
92
|
+
): string => {
|
|
93
|
+
const parts = [`Bearer resource_metadata="${protectedResourceMetadataUrl(config, connector)}"`];
|
|
94
|
+
if (error) parts.push(`error="${error}"`);
|
|
95
|
+
if (description) parts.push(`error_description="${description}"`);
|
|
96
|
+
return parts.join(', ');
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const extractBearer = (req: IncomingMessage): string | undefined => {
|
|
100
|
+
const header = req.headers.authorization;
|
|
101
|
+
if (!header) return undefined;
|
|
102
|
+
const [scheme, ...rest] = header.split(' ');
|
|
103
|
+
if (scheme !== 'Bearer' || rest.length === 0) return undefined;
|
|
104
|
+
const token = rest.join(' ').trim();
|
|
105
|
+
return token.length > 0 ? token : undefined;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Requires a bearer token and hands it to the SDK as-is. Throws
|
|
110
|
+
* {@link UnauthorizedError} (401 + challenge) when it is missing, which is the
|
|
111
|
+
* signal an MCP client needs to start the OAuth flow. Validity is decided by
|
|
112
|
+
* customer-api on each call; an expired or revoked key surfaces there.
|
|
113
|
+
*
|
|
114
|
+
* A pinned connector also fixes the SDK's `project`. This is `project` rather than a
|
|
115
|
+
* `defaultHeaders` entry deliberately: code the model writes inside the execution tool can still
|
|
116
|
+
* call `client.withOptions({ project })` to reach another project the credential covers, which a
|
|
117
|
+
* default header would silently override. On the unpinned `/mcp` connector no project is set at
|
|
118
|
+
* all, and choosing one is the caller's job.
|
|
119
|
+
*/
|
|
120
|
+
export const requireBearer = (
|
|
121
|
+
req: IncomingMessage,
|
|
122
|
+
config: OAuthConfig,
|
|
123
|
+
connector: Connector = ORIGIN_CONNECTOR,
|
|
124
|
+
): Partial<ClientOptions> => {
|
|
125
|
+
const token = extractBearer(req);
|
|
126
|
+
if (!token) {
|
|
127
|
+
throw new UnauthorizedError('Missing bearer token', bearerChallenge(config, connector));
|
|
128
|
+
}
|
|
129
|
+
// Assigned rather than conditionally spread: a spread of `cond && { project }` is not checked
|
|
130
|
+
// against `ClientOptions`, so on an SDK without the option the pin would compile and then
|
|
131
|
+
// silently vanish. This way the dependency is enforced by the build.
|
|
132
|
+
const options: Partial<ClientOptions> = { bearerToken: token };
|
|
133
|
+
if (connector.kind === 'project') {
|
|
134
|
+
options.project = connector.projectId;
|
|
135
|
+
}
|
|
136
|
+
return options;
|
|
137
|
+
};
|
package/src/options.ts
CHANGED
|
@@ -5,6 +5,7 @@ import yargs from 'yargs';
|
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
6
|
import z from 'zod';
|
|
7
7
|
import { readEnv } from './util';
|
|
8
|
+
import type { OAuthConfig } from './oauth';
|
|
8
9
|
|
|
9
10
|
export type CLIOptions = McpOptions & {
|
|
10
11
|
debug: boolean;
|
|
@@ -25,6 +26,13 @@ export type McpOptions = {
|
|
|
25
26
|
codeBlockedMethods?: string[] | undefined;
|
|
26
27
|
codeExecutionMode: McpCodeExecutionMode;
|
|
27
28
|
customInstructionsPath?: string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Remote OAuth resource-server config. When set (http transport), every request
|
|
31
|
+
* must carry a bearer and a missing one is answered with a 401 challenge that
|
|
32
|
+
* points at the authorization server; when unset the server keeps the legacy
|
|
33
|
+
* optional-bearer behavior (local stdio + API-key installs).
|
|
34
|
+
*/
|
|
35
|
+
oauth?: OAuthConfig | undefined;
|
|
28
36
|
};
|
|
29
37
|
|
|
30
38
|
export type McpCodeExecutionMode = 'stainless-sandbox' | 'local';
|
|
@@ -114,6 +122,16 @@ export function parseCLIOptions(): CLIOptions {
|
|
|
114
122
|
default: 'stdio',
|
|
115
123
|
description: 'What transport to use; stdio for local servers or http for remote servers',
|
|
116
124
|
})
|
|
125
|
+
.option('oauth-issuer', {
|
|
126
|
+
type: 'string',
|
|
127
|
+
default: readEnv('MCP_OAUTH_ISSUER'),
|
|
128
|
+
description: 'Authorization server issuer URL. Enables remote OAuth resource-server mode.',
|
|
129
|
+
})
|
|
130
|
+
.option('oauth-resource-base-url', {
|
|
131
|
+
type: 'string',
|
|
132
|
+
default: readEnv('MCP_OAUTH_RESOURCE_BASE_URL'),
|
|
133
|
+
description: 'Public base URL of this resource server, used to build metadata and resource URLs.',
|
|
134
|
+
})
|
|
117
135
|
.env('MCP_SERVER')
|
|
118
136
|
.version(true)
|
|
119
137
|
.help();
|
|
@@ -144,6 +162,10 @@ export function parseCLIOptions(): CLIOptions {
|
|
|
144
162
|
: process.stderr.isTTY ? 'pretty'
|
|
145
163
|
: 'json';
|
|
146
164
|
|
|
165
|
+
// When unset, an http server runs in legacy optional-bearer mode (a static
|
|
166
|
+
// token in the Authorization header). Remote account-based auth needs this set.
|
|
167
|
+
const oauth = buildOAuthConfig(argv);
|
|
168
|
+
|
|
147
169
|
return {
|
|
148
170
|
...(includeCodeTool !== undefined && { includeCodeTool }),
|
|
149
171
|
...(includeDocsTools !== undefined && { includeDocsTools }),
|
|
@@ -156,6 +178,7 @@ export function parseCLIOptions(): CLIOptions {
|
|
|
156
178
|
codeBlockedMethods: argv.codeBlockedMethods,
|
|
157
179
|
codeExecutionMode,
|
|
158
180
|
customInstructionsPath: argv.customInstructionsPath,
|
|
181
|
+
...(oauth && { oauth }),
|
|
159
182
|
transport,
|
|
160
183
|
logFormat,
|
|
161
184
|
port: argv.port,
|
|
@@ -163,6 +186,22 @@ export function parseCLIOptions(): CLIOptions {
|
|
|
163
186
|
};
|
|
164
187
|
}
|
|
165
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Assembles the OAuth resource-server config from CLI/env, or returns undefined
|
|
191
|
+
* when either field is absent (legacy optional-bearer mode). Both the issuer and
|
|
192
|
+
* this server's public base URL are required together.
|
|
193
|
+
*/
|
|
194
|
+
const buildOAuthConfig = (argv: {
|
|
195
|
+
oauthIssuer?: string | undefined;
|
|
196
|
+
oauthResourceBaseUrl?: string | undefined;
|
|
197
|
+
}): OAuthConfig | undefined => {
|
|
198
|
+
const { oauthIssuer, oauthResourceBaseUrl } = argv;
|
|
199
|
+
if (!oauthIssuer || !oauthResourceBaseUrl) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
return { issuer: oauthIssuer, resourceBaseUrl: oauthResourceBaseUrl };
|
|
203
|
+
};
|
|
204
|
+
|
|
166
205
|
const coerceArray = <T extends z.ZodTypeAny>(zodType: T) =>
|
|
167
206
|
z.preprocess(
|
|
168
207
|
(val) =>
|