@typelets/mcp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/profile.js +3 -0
- package/dist/profile.js.map +1 -1
- package/dist/tools/score_against_rubric.d.ts +18 -0
- package/dist/tools/score_against_rubric.js +49 -0
- package/dist/tools/score_against_rubric.js.map +1 -0
- package/dist/tools/suggest_followup_questions.d.ts +15 -0
- package/dist/tools/suggest_followup_questions.js +45 -0
- package/dist/tools/suggest_followup_questions.js.map +1 -0
- package/dist/tools/summarize_recording.d.ts +13 -0
- package/dist/tools/summarize_recording.js +29 -0
- package/dist/tools/summarize_recording.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,16 @@ Profiles:
|
|
|
110
110
|
|
|
111
111
|
Tools marked `destructive` carry the MCP `destructiveHint: true` annotation so host clients (Claude Desktop, Cline, Cursor) prompt the user before invocation.
|
|
112
112
|
|
|
113
|
+
### Session intelligence (3 tools, interviewer profile only)
|
|
114
|
+
|
|
115
|
+
| Tool | Reads | Input |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
| `summarize_recording` | `GET /workspaces/:id/recordings/:rid/timeline?mode=summary` | `workspaceId`, `recordingId` |
|
|
118
|
+
| `score_against_rubric` | `GET /workspaces/:id/recordings/:rid/timeline?mode=score` | `workspaceId`, `recordingId` |
|
|
119
|
+
| `suggest_followup_questions` | `GET /workspaces/:id/recordings/active/timeline` | `workspaceId` |
|
|
120
|
+
|
|
121
|
+
Each tool returns a structured timeline (per-file content checkpoints sampled across the session + chronological Run-button events) so the host LLM can write the prose summary, score, or follow-up questions itself. `score_against_rubric` also attaches the workspace's inline rubric + criteria; calls against a workspace with no applied problem return a friendly error pointing the user to `apply_problem_to_workspace`. `suggest_followup_questions` operates on the workspace's currently-active recording (last 5 minutes), and surfaces a clear error if no recording is in progress.
|
|
122
|
+
|
|
113
123
|
## Layout
|
|
114
124
|
|
|
115
125
|
```
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Phase 2 write tools are profile-gated at registration time.
|
|
13
13
|
* interviewer-only tools are not registered when TYPELETS_PROFILE=candidate.
|
|
14
|
-
* Phase 3
|
|
14
|
+
* Phase 3 session-intelligence tools are also profile-gated at registration.
|
|
15
15
|
*/
|
|
16
16
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
17
17
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -33,6 +33,9 @@ import { registerApplyProblemToWorkspace } from './tools/apply_problem_to_worksp
|
|
|
33
33
|
import { registerSaveProblemToLibrary } from './tools/save_problem_to_library.js';
|
|
34
34
|
import { registerEditProblem } from './tools/edit_problem.js';
|
|
35
35
|
import { registerDeleteProblem } from './tools/delete_problem.js';
|
|
36
|
+
import { registerSummarizeRecording } from './tools/summarize_recording.js';
|
|
37
|
+
import { registerScoreAgainstRubric } from './tools/score_against_rubric.js';
|
|
38
|
+
import { registerSuggestFollowupQuestions } from './tools/suggest_followup_questions.js';
|
|
36
39
|
async function main() {
|
|
37
40
|
const env = readEnv();
|
|
38
41
|
const client = createClient(env);
|
|
@@ -64,6 +67,10 @@ async function main() {
|
|
|
64
67
|
registerSaveProblemToLibrary(server, client, env);
|
|
65
68
|
registerEditProblem(server, client, env);
|
|
66
69
|
registerDeleteProblem(server, client, env);
|
|
70
|
+
// Phase 3 session-intelligence tools (interviewer-only).
|
|
71
|
+
registerSummarizeRecording(server, client, env);
|
|
72
|
+
registerScoreAgainstRubric(server, client, env);
|
|
73
|
+
registerSuggestFollowupQuestions(server, client, env);
|
|
67
74
|
const transport = new StdioServerTransport();
|
|
68
75
|
await server.connect(transport);
|
|
69
76
|
// Log to stderr (stdout is the MCP transport). Surfaces in the client's
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/profile.js
CHANGED
|
@@ -14,6 +14,9 @@ export const INTERVIEWER_ONLY_TOOLS = new Set([
|
|
|
14
14
|
'save_problem_to_library',
|
|
15
15
|
'edit_problem',
|
|
16
16
|
'delete_problem',
|
|
17
|
+
'summarize_recording',
|
|
18
|
+
'score_against_rubric',
|
|
19
|
+
'suggest_followup_questions',
|
|
17
20
|
]);
|
|
18
21
|
export function toolAllowedForProfile(toolName, profile) {
|
|
19
22
|
if (profile === 'interviewer')
|
package/dist/profile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IACjE,kBAAkB;IAClB,4BAA4B;IAC5B,yBAAyB;IACzB,cAAc;IACd,gBAAgB;
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IACjE,kBAAkB;IAClB,4BAA4B;IAC5B,yBAAyB;IACzB,cAAc;IACd,gBAAgB;IAChB,qBAAqB;IACrB,sBAAsB;IACtB,4BAA4B;CAC7B,CAAC,CAAC;AAEH,MAAM,UAAU,qBAAqB,CAAC,QAAgB,EAAE,OAAuB;IAC7E,IAAI,OAAO,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAAU,EACV,GAAQ;IAER,IAAI,GAAG,CAAC,OAAO,KAAK,aAAa;QAAE,OAAO,OAAO,CAAC;IAClD,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAC5B,OAAO,IAAI,CAAC,MAAM,CAAC;IACnB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACrB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACrB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* score_against_rubric: return a structured timeline plus the workspace's
|
|
3
|
+
* inline interview rubric + criteria so the host LLM can score the
|
|
4
|
+
* candidate against per-criterion.
|
|
5
|
+
*
|
|
6
|
+
* Workspaces with no inline rubric or criteria are not scorable - the
|
|
7
|
+
* apply-problem handler copies rubric/criteria onto the workspace
|
|
8
|
+
* directly; bare workspaces have nothing meaningful to score against.
|
|
9
|
+
* The api returns 404 + code=no_source_problem in that case, which we
|
|
10
|
+
* translate to a friendly error so the host LLM nudges the user to
|
|
11
|
+
* apply or save a problem first.
|
|
12
|
+
*
|
|
13
|
+
* Interviewer-only.
|
|
14
|
+
*/
|
|
15
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
16
|
+
import type { TypeletsClient } from '../client.js';
|
|
17
|
+
import type { Env } from '../env.js';
|
|
18
|
+
export declare function registerScoreAgainstRubric(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TypeletsApiError } from '../client.js';
|
|
3
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
4
|
+
import { ok, fail } from './_shared.js';
|
|
5
|
+
function isNoSourceProblem(err) {
|
|
6
|
+
return (err instanceof TypeletsApiError &&
|
|
7
|
+
err.status === 404 &&
|
|
8
|
+
err.body !== null &&
|
|
9
|
+
typeof err.body === 'object' &&
|
|
10
|
+
err.body.code === 'no_source_problem');
|
|
11
|
+
}
|
|
12
|
+
export function registerScoreAgainstRubric(server, client, env) {
|
|
13
|
+
if (!toolAllowedForProfile('score_against_rubric', env.profile))
|
|
14
|
+
return;
|
|
15
|
+
server.registerTool('score_against_rubric', {
|
|
16
|
+
title: 'Score a recording against the rubric',
|
|
17
|
+
description: "Fetch the structured timeline of a recording plus the workspace's inline interview rubric + criteria, so the host LLM can score the candidate per criterion. Only works on workspaces that have a problem applied (use apply_problem_to_workspace or save_problem_to_library first if needed).",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
20
|
+
recordingId: z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.describe('The recording id from list_recordings.'),
|
|
24
|
+
},
|
|
25
|
+
annotations: { destructiveHint: false },
|
|
26
|
+
}, async ({ workspaceId, recordingId }) => {
|
|
27
|
+
try {
|
|
28
|
+
const payload = await client.get(`/workspaces/${encodeURIComponent(workspaceId)}/recordings/${encodeURIComponent(recordingId)}/timeline?mode=score&samples=10`);
|
|
29
|
+
const summary = `Loaded recording ${recordingId} and rubric for workspace ${payload.workspace.name} (${payload.problem.criteria.length} criteria).`;
|
|
30
|
+
return ok(summary, payload);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (isNoSourceProblem(err)) {
|
|
34
|
+
return {
|
|
35
|
+
isError: true,
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: 'text',
|
|
39
|
+
text: 'This workspace is not derived from a problem (no rubric or criteria attached), so there is no rubric to score against. Apply a problem to the workspace first.',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
structuredContent: { error: { code: 'no_source_problem' } },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return fail(err);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=score_against_rubric.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score_against_rubric.js","sourceRoot":"","sources":["../../src/tools/score_against_rubric.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAexC,SAAS,iBAAiB,CAAC,GAAY;IACrC,OAAO,CACL,GAAG,YAAY,gBAAgB;QAC/B,GAAG,CAAC,MAAM,KAAK,GAAG;QAClB,GAAG,CAAC,IAAI,KAAK,IAAI;QACjB,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC3B,GAAG,CAAC,IAA0B,CAAC,IAAI,KAAK,mBAAmB,CAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,MAAiB,EACjB,MAAsB,EACtB,GAAQ;IAER,IAAI,CAAC,qBAAqB,CAAC,sBAAsB,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAExE,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,sCAAsC;QAC7C,WAAW,EACT,gSAAgS;QAClS,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC5D,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,wCAAwC,CAAC;SACtD;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,GAAG,CAC9B,eAAe,kBAAkB,CAAC,WAAW,CAAC,eAAe,kBAAkB,CAAC,WAAW,CAAC,iCAAiC,CAC9H,CAAC;YACF,MAAM,OAAO,GAAG,oBAAoB,WAAW,6BAA6B,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,aAAa,CAAC;YACpJ,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3B,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,gKAAgK;yBACvK;qBACF;oBACD,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE;iBAC5D,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* suggest_followup_questions: return a structured slice of the workspace's
|
|
3
|
+
* ACTIVE recording (last 5 minutes of activity + current file state) so the
|
|
4
|
+
* host LLM can propose questions for the interviewer to ask next.
|
|
5
|
+
*
|
|
6
|
+
* The api resolves the active recording for us; the tool takes only the
|
|
7
|
+
* workspaceId. 404 with code=no_active_recording means the interviewer
|
|
8
|
+
* hasn't started recording the session yet.
|
|
9
|
+
*
|
|
10
|
+
* Interviewer-only.
|
|
11
|
+
*/
|
|
12
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
|
+
import type { TypeletsClient } from '../client.js';
|
|
14
|
+
import type { Env } from '../env.js';
|
|
15
|
+
export declare function registerSuggestFollowupQuestions(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TypeletsApiError } from '../client.js';
|
|
3
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
4
|
+
import { ok, fail } from './_shared.js';
|
|
5
|
+
function isNoActiveRecording(err) {
|
|
6
|
+
return (err instanceof TypeletsApiError &&
|
|
7
|
+
err.status === 404 &&
|
|
8
|
+
err.body !== null &&
|
|
9
|
+
typeof err.body === 'object' &&
|
|
10
|
+
err.body.code === 'no_active_recording');
|
|
11
|
+
}
|
|
12
|
+
export function registerSuggestFollowupQuestions(server, client, env) {
|
|
13
|
+
if (!toolAllowedForProfile('suggest_followup_questions', env.profile))
|
|
14
|
+
return;
|
|
15
|
+
server.registerTool('suggest_followup_questions', {
|
|
16
|
+
title: 'Suggest follow-up questions for the active recording',
|
|
17
|
+
description: "Fetch the last 5 minutes of activity from the workspace's active recording plus current file state, so the host LLM can suggest 2-3 follow-up questions the interviewer might ask. Requires an active recording; start one with the interview panel first.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
20
|
+
},
|
|
21
|
+
annotations: { destructiveHint: false },
|
|
22
|
+
}, async ({ workspaceId }) => {
|
|
23
|
+
try {
|
|
24
|
+
const payload = await client.get(`/workspaces/${encodeURIComponent(workspaceId)}/recordings/active/timeline?samples=6`);
|
|
25
|
+
const summary = `Loaded live timeline for workspace ${payload.workspace.name} (${payload.files.length} files in view).`;
|
|
26
|
+
return ok(summary, payload);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
if (isNoActiveRecording(err)) {
|
|
30
|
+
return {
|
|
31
|
+
isError: true,
|
|
32
|
+
content: [
|
|
33
|
+
{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: 'No active recording in this workspace. Start a recording with the interview panel first.',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
structuredContent: { error: { code: 'no_active_recording' } },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return fail(err);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=suggest_followup_questions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest_followup_questions.js","sourceRoot":"","sources":["../../src/tools/suggest_followup_questions.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAUxC,SAAS,mBAAmB,CAAC,GAAY;IACvC,OAAO,CACL,GAAG,YAAY,gBAAgB;QAC/B,GAAG,CAAC,MAAM,KAAK,GAAG;QAClB,GAAG,CAAC,IAAI,KAAK,IAAI;QACjB,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAC3B,GAAG,CAAC,IAA0B,CAAC,IAAI,KAAK,qBAAqB,CAC/D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,MAAiB,EACjB,MAAsB,EACtB,GAAQ;IAER,IAAI,CAAC,qBAAqB,CAAC,4BAA4B,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAE9E,MAAM,CAAC,YAAY,CACjB,4BAA4B,EAC5B;QACE,KAAK,EAAE,sDAAsD;QAC7D,WAAW,EACT,4PAA4P;QAC9P,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;SAC7D;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,GAAG,CAC9B,eAAe,kBAAkB,CAAC,WAAW,CAAC,uCAAuC,CACtF,CAAC;YACF,MAAM,OAAO,GAAG,sCAAsC,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,KAAK,CAAC,MAAM,kBAAkB,CAAC;YACxH,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,0FAA0F;yBACjG;qBACF;oBACD,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,EAAE;iBAC9D,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* summarize_recording: return a structured timeline of a frozen recording.
|
|
3
|
+
*
|
|
4
|
+
* The host LLM consumes the JSON and writes the actual prose summary. The
|
|
5
|
+
* tool itself just calls the api's /timeline?mode=summary endpoint and
|
|
6
|
+
* passes the response through.
|
|
7
|
+
*
|
|
8
|
+
* Interviewer-only.
|
|
9
|
+
*/
|
|
10
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
import type { TypeletsClient } from '../client.js';
|
|
12
|
+
import type { Env } from '../env.js';
|
|
13
|
+
export declare function registerSummarizeRecording(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
3
|
+
import { ok, fail } from './_shared.js';
|
|
4
|
+
export function registerSummarizeRecording(server, client, env) {
|
|
5
|
+
if (!toolAllowedForProfile('summarize_recording', env.profile))
|
|
6
|
+
return;
|
|
7
|
+
server.registerTool('summarize_recording', {
|
|
8
|
+
title: 'Summarize a recording',
|
|
9
|
+
description: 'Fetch a structured timeline of a recorded interview session for the host LLM to summarize. Includes per-file content checkpoints sampled across the session and a chronological list of Run-button invocations. Use list_recordings first to obtain the recordingId.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
12
|
+
recordingId: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.describe('The recording id from list_recordings.'),
|
|
16
|
+
},
|
|
17
|
+
annotations: { destructiveHint: false },
|
|
18
|
+
}, async ({ workspaceId, recordingId }) => {
|
|
19
|
+
try {
|
|
20
|
+
const payload = await client.get(`/workspaces/${encodeURIComponent(workspaceId)}/recordings/${encodeURIComponent(recordingId)}/timeline?mode=summary&samples=10`);
|
|
21
|
+
const summary = `Loaded recording ${recordingId} for workspace ${payload.workspace.name} (${payload.runs.length} runs across ${payload.files.length} files, ${payload.recording.durationMs}ms).`;
|
|
22
|
+
return ok(summary, payload);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return fail(err);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=summarize_recording.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarize_recording.js","sourceRoot":"","sources":["../../src/tools/summarize_recording.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAUxC,MAAM,UAAU,0BAA0B,CACxC,MAAiB,EACjB,MAAsB,EACtB,GAAQ;IAER,IAAI,CAAC,qBAAqB,CAAC,qBAAqB,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAEvE,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,sQAAsQ;QACxQ,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC5D,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,wCAAwC,CAAC;SACtD;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,GAAG,CAC9B,eAAe,kBAAkB,CAAC,WAAW,CAAC,eAAe,kBAAkB,CAAC,WAAW,CAAC,mCAAmC,CAChI,CAAC;YACF,MAAM,OAAO,GAAG,oBAAoB,WAAW,kBAAkB,OAAO,CAAC,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,MAAM,gBAAgB,OAAO,CAAC,KAAK,CAAC,MAAM,WAAW,OAAO,CAAC,SAAS,CAAC,UAAU,MAAM,CAAC;YACjM,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|