@typelets/mcp 0.2.0 → 0.4.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 +23 -0
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/profile.js +4 -0
- package/dist/profile.js.map +1 -1
- package/dist/tools/append_to_file.d.ts +8 -0
- package/dist/tools/append_to_file.js +26 -0
- package/dist/tools/append_to_file.js.map +1 -0
- package/dist/tools/create_folder.d.ts +8 -0
- package/dist/tools/create_folder.js +25 -0
- package/dist/tools/create_folder.js.map +1 -0
- package/dist/tools/delete_folder.d.ts +8 -0
- package/dist/tools/delete_folder.js +28 -0
- package/dist/tools/delete_folder.js.map +1 -0
- package/dist/tools/delete_workspace.d.ts +9 -0
- package/dist/tools/delete_workspace.js +24 -0
- package/dist/tools/delete_workspace.js.map +1 -0
- package/dist/tools/move_path.d.ts +9 -0
- package/dist/tools/move_path.js +29 -0
- package/dist/tools/move_path.js.map +1 -0
- 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/dist/tools/whoami.d.ts +9 -0
- package/dist/tools/whoami.js +25 -0
- package/dist/tools/whoami.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,6 +110,29 @@ 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
|
+
|
|
123
|
+
### Completeness: file/folder + lifecycle (6 tools)
|
|
124
|
+
|
|
125
|
+
| Tool | Writes | Profile | `destructive` |
|
|
126
|
+
| --- | --- | --- | --- |
|
|
127
|
+
| `move_path` | `POST /workspaces/:id/move` | both | |
|
|
128
|
+
| `create_folder` | `POST /workspaces/:id/folders` | both | |
|
|
129
|
+
| `delete_folder` | `DELETE /workspaces/:id/folders/:folderId` | both | ✓ |
|
|
130
|
+
| `append_to_file` | `PATCH /workspaces/:id/files/:fileId/append` | both | |
|
|
131
|
+
| `delete_workspace` | `DELETE /workspaces/:id` | interviewer only | ✓ |
|
|
132
|
+
| `whoami` | `GET /auth/me` | both | |
|
|
133
|
+
|
|
134
|
+
`move_path` renames or moves any node (file or folder) to a full destination path, creating intermediate folders as needed; it rejects moving a folder into its own subtree. `create_folder` makes an empty folder (idempotent). `delete_folder` removes a folder and everything under it. `append_to_file` adds to the end of a file without re-sending its content. `delete_workspace` is interviewer-only and owner-gated by the API. `whoami` reports the identity + profile the server is running as.
|
|
135
|
+
|
|
113
136
|
## Layout
|
|
114
137
|
|
|
115
138
|
```
|
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,15 @@ 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';
|
|
39
|
+
import { registerMovePath } from './tools/move_path.js';
|
|
40
|
+
import { registerCreateFolder } from './tools/create_folder.js';
|
|
41
|
+
import { registerDeleteFolder } from './tools/delete_folder.js';
|
|
42
|
+
import { registerAppendToFile } from './tools/append_to_file.js';
|
|
43
|
+
import { registerDeleteWorkspace } from './tools/delete_workspace.js';
|
|
44
|
+
import { registerWhoami } from './tools/whoami.js';
|
|
36
45
|
async function main() {
|
|
37
46
|
const env = readEnv();
|
|
38
47
|
const client = createClient(env);
|
|
@@ -64,6 +73,17 @@ async function main() {
|
|
|
64
73
|
registerSaveProblemToLibrary(server, client, env);
|
|
65
74
|
registerEditProblem(server, client, env);
|
|
66
75
|
registerDeleteProblem(server, client, env);
|
|
76
|
+
// Phase 3 session-intelligence tools (interviewer-only).
|
|
77
|
+
registerSummarizeRecording(server, client, env);
|
|
78
|
+
registerScoreAgainstRubric(server, client, env);
|
|
79
|
+
registerSuggestFollowupQuestions(server, client, env);
|
|
80
|
+
// Phase 2.2 completeness tools (delete_workspace is interviewer-only).
|
|
81
|
+
registerMovePath(server, client, env);
|
|
82
|
+
registerCreateFolder(server, client, env);
|
|
83
|
+
registerDeleteFolder(server, client, env);
|
|
84
|
+
registerAppendToFile(server, client, env);
|
|
85
|
+
registerDeleteWorkspace(server, client, env);
|
|
86
|
+
registerWhoami(server, client, env);
|
|
67
87
|
const transport = new StdioServerTransport();
|
|
68
88
|
await server.connect(transport);
|
|
69
89
|
// 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,10 @@ 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',
|
|
20
|
+
'delete_workspace',
|
|
17
21
|
]);
|
|
18
22
|
export function toolAllowedForProfile(toolName, profile) {
|
|
19
23
|
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;IAC5B,kBAAkB;CACnB,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,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* append_to_file: append text to the end of a file without re-sending the
|
|
3
|
+
* whole content. Both profiles.
|
|
4
|
+
*/
|
|
5
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
+
import type { TypeletsClient } from '../client.js';
|
|
7
|
+
import type { Env } from '../env.js';
|
|
8
|
+
export declare function registerAppendToFile(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
3
|
+
import { ok, fail } from './_shared.js';
|
|
4
|
+
export function registerAppendToFile(server, client, env) {
|
|
5
|
+
if (!toolAllowedForProfile('append_to_file', env.profile))
|
|
6
|
+
return;
|
|
7
|
+
server.registerTool('append_to_file', {
|
|
8
|
+
title: 'Append to a file',
|
|
9
|
+
description: 'Append text to the end of an existing file without re-sending its full content. Get fileId from list_workspace_files. The file must already exist (use create_file otherwise).',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
12
|
+
fileId: z.string().min(1).describe('The file id from list_workspace_files.'),
|
|
13
|
+
text: z.string().min(1).describe('Text to append at the end of the file.'),
|
|
14
|
+
},
|
|
15
|
+
annotations: { destructiveHint: false },
|
|
16
|
+
}, async ({ workspaceId, fileId, text }) => {
|
|
17
|
+
try {
|
|
18
|
+
const out = await client.patch(`/workspaces/${encodeURIComponent(workspaceId)}/files/${encodeURIComponent(fileId)}/append`, { text });
|
|
19
|
+
return ok(`Appended to file (${out.bytes} bytes total).`, out);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return fail(err);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=append_to_file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append_to_file.js","sourceRoot":"","sources":["../../src/tools/append_to_file.ts"],"names":[],"mappings":"AAKA,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;AAOxC,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAsB,EAAE,GAAQ;IACtF,IAAI,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAElE,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,gLAAgL;QAClL,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC5D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,wCAAwC,CAAC;SAC3E;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,eAAe,kBAAkB,CAAC,WAAW,CAAC,UAAU,kBAAkB,CAAC,MAAM,CAAC,SAAS,EAC3F,EAAE,IAAI,EAAE,CACT,CAAC;YACF,OAAO,EAAE,CAAC,qBAAqB,GAAG,CAAC,KAAK,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* create_folder: create an empty folder (with intermediate folders) at a path.
|
|
3
|
+
* Idempotent if the folder already exists. Both profiles.
|
|
4
|
+
*/
|
|
5
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
+
import type { TypeletsClient } from '../client.js';
|
|
7
|
+
import type { Env } from '../env.js';
|
|
8
|
+
export declare function registerCreateFolder(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
3
|
+
import { ok, fail } from './_shared.js';
|
|
4
|
+
export function registerCreateFolder(server, client, env) {
|
|
5
|
+
if (!toolAllowedForProfile('create_folder', env.profile))
|
|
6
|
+
return;
|
|
7
|
+
server.registerTool('create_folder', {
|
|
8
|
+
title: 'Create a folder',
|
|
9
|
+
description: 'Create an empty folder at a slash-separated path; intermediate folders are created as needed. Idempotent if the folder already exists. Errors if a file occupies the path.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
12
|
+
path: z.string().min(1).describe('Folder path, e.g. "src/lib".'),
|
|
13
|
+
},
|
|
14
|
+
annotations: { destructiveHint: false },
|
|
15
|
+
}, async ({ workspaceId, path }) => {
|
|
16
|
+
try {
|
|
17
|
+
const out = await client.post(`/workspaces/${encodeURIComponent(workspaceId)}/folders`, { path });
|
|
18
|
+
return ok(`Created folder ${out.path}.`, out);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
return fail(err);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=create_folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create_folder.js","sourceRoot":"","sources":["../../src/tools/create_folder.ts"],"names":[],"mappings":"AAKA,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;AASxC,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAsB,EAAE,GAAQ;IACtF,IAAI,CAAC,qBAAqB,CAAC,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAEjE,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,4KAA4K;QAC9K,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC5D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;SACjE;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,eAAe,kBAAkB,CAAC,WAAW,CAAC,UAAU,EACxD,EAAE,IAAI,EAAE,CACT,CAAC;YACF,OAAO,EAAE,CAAC,kBAAkB,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* delete_folder: recursively delete a folder and everything under it.
|
|
3
|
+
* Destructive. Both profiles (editors manage their own tree).
|
|
4
|
+
*/
|
|
5
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
+
import type { TypeletsClient } from '../client.js';
|
|
7
|
+
import type { Env } from '../env.js';
|
|
8
|
+
export declare function registerDeleteFolder(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
3
|
+
import { ok, fail } from './_shared.js';
|
|
4
|
+
export function registerDeleteFolder(server, client, env) {
|
|
5
|
+
if (!toolAllowedForProfile('delete_folder', env.profile))
|
|
6
|
+
return;
|
|
7
|
+
server.registerTool('delete_folder', {
|
|
8
|
+
title: 'Delete a folder (recursive)',
|
|
9
|
+
description: 'Permanently delete a folder and ALL files and subfolders inside it. Get folderId from list_workspace_files with includeFolders=true. This cannot be undone.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
12
|
+
folderId: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.describe('The folder id from list_workspace_files (includeFolders=true).'),
|
|
16
|
+
},
|
|
17
|
+
annotations: { destructiveHint: true },
|
|
18
|
+
}, async ({ workspaceId, folderId }) => {
|
|
19
|
+
try {
|
|
20
|
+
const out = await client.delete(`/workspaces/${encodeURIComponent(workspaceId)}/folders/${encodeURIComponent(folderId)}`);
|
|
21
|
+
return ok(`Deleted folder and ${out.deleted} item(s).`, out);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
return fail(err);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=delete_folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete_folder.js","sourceRoot":"","sources":["../../src/tools/delete_folder.ts"],"names":[],"mappings":"AAKA,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;AAMxC,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAsB,EAAE,GAAQ;IACtF,IAAI,CAAC,qBAAqB,CAAC,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAEjE,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,6JAA6J;QAC/J,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC5D,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,gEAAgE,CAAC;SAC9E;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;KACvC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAC7B,eAAe,kBAAkB,CAAC,WAAW,CAAC,YAAY,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CACzF,CAAC;YACF,OAAO,EAAE,CAAC,sBAAsB,GAAG,CAAC,OAAO,WAAW,EAAE,GAAG,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* delete_workspace: permanently delete a workspace. Interviewer-only and
|
|
3
|
+
* destructive. Wraps the existing owner-gated DELETE /workspaces/:id (the api
|
|
4
|
+
* returns 403 if the caller is not the owner).
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import type { TypeletsClient } from '../client.js';
|
|
8
|
+
import type { Env } from '../env.js';
|
|
9
|
+
export declare function registerDeleteWorkspace(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
3
|
+
import { ok, fail } from './_shared.js';
|
|
4
|
+
export function registerDeleteWorkspace(server, client, env) {
|
|
5
|
+
if (!toolAllowedForProfile('delete_workspace', env.profile))
|
|
6
|
+
return;
|
|
7
|
+
server.registerTool('delete_workspace', {
|
|
8
|
+
title: 'Delete a workspace',
|
|
9
|
+
description: 'Permanently delete a workspace and all its files, recordings, and members. Only the workspace owner can do this. This cannot be undone.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
workspaceId: z.string().min(1).describe('The workspace id to permanently delete.'),
|
|
12
|
+
},
|
|
13
|
+
annotations: { destructiveHint: true },
|
|
14
|
+
}, async ({ workspaceId }) => {
|
|
15
|
+
try {
|
|
16
|
+
await client.delete(`/workspaces/${encodeURIComponent(workspaceId)}`);
|
|
17
|
+
return ok('Workspace deleted.', { workspaceId, deleted: true });
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
return fail(err);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=delete_workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete_workspace.js","sourceRoot":"","sources":["../../src/tools/delete_workspace.ts"],"names":[],"mappings":"AAMA,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;AAExC,MAAM,UAAU,uBAAuB,CACrC,MAAiB,EACjB,MAAsB,EACtB,GAAQ;IAER,IAAI,CAAC,qBAAqB,CAAC,kBAAkB,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAEpE,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,yIAAyI;QAC3I,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,yCAAyC,CAAC;SACnF;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;KACvC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,MAAM,CAAO,eAAe,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAC5E,OAAO,EAAE,CAAC,oBAAoB,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* move_path: rename or move a file/folder to a new path. The destination's
|
|
3
|
+
* parent folders are created as needed. Works on files and folders alike.
|
|
4
|
+
* Available to both profiles (editors manage their own tree).
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import type { TypeletsClient } from '../client.js';
|
|
8
|
+
import type { Env } from '../env.js';
|
|
9
|
+
export declare function registerMovePath(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 registerMovePath(server, client, env) {
|
|
5
|
+
if (!toolAllowedForProfile('move_path', env.profile))
|
|
6
|
+
return;
|
|
7
|
+
server.registerTool('move_path', {
|
|
8
|
+
title: 'Move or rename a file or folder',
|
|
9
|
+
description: 'Move or rename a node (file OR folder) to a new full path. Parent folders are created as needed. Get nodeId from list_workspace_files (use includeFolders for folders). Moving a folder into its own subtree is rejected.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
workspaceId: z.string().min(1).describe('The workspace id.'),
|
|
12
|
+
nodeId: z.string().min(1).describe('The file or folder id from list_workspace_files.'),
|
|
13
|
+
destinationPath: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1)
|
|
16
|
+
.describe('Full destination path, e.g. "src/lib/auth.ts".'),
|
|
17
|
+
},
|
|
18
|
+
annotations: { destructiveHint: false },
|
|
19
|
+
}, async ({ workspaceId, nodeId, destinationPath }) => {
|
|
20
|
+
try {
|
|
21
|
+
const out = await client.post(`/workspaces/${encodeURIComponent(workspaceId)}/files/move`, { nodeId, destinationPath });
|
|
22
|
+
return ok(`Moved to ${out.path}.`, out);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return fail(err);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=move_path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"move_path.js","sourceRoot":"","sources":["../../src/tools/move_path.ts"],"names":[],"mappings":"AAMA,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;AASxC,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,MAAsB,EAAE,GAAQ;IAClF,IAAI,CAAC,qBAAqB,CAAC,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAE7D,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;QACE,KAAK,EAAE,iCAAiC;QACxC,WAAW,EACT,2NAA2N;QAC7N,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC5D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kDAAkD,CAAC;YACtF,eAAe,EAAE,CAAC;iBACf,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,gDAAgD,CAAC;SAC9D;QACD,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,EAAE;QACjD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,eAAe,kBAAkB,CAAC,WAAW,CAAC,aAAa,EAC3D,EAAE,MAAM,EAAE,eAAe,EAAE,CAC5B,CAAC;YACF,OAAO,EAAE,CAAC,YAAY,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whoami: return the authenticated caller's identity. Wraps GET /auth/me so
|
|
3
|
+
* the host LLM can confirm which account + profile it's operating as. Both
|
|
4
|
+
* profiles.
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import type { TypeletsClient } from '../client.js';
|
|
8
|
+
import type { Env } from '../env.js';
|
|
9
|
+
export declare function registerWhoami(server: McpServer, client: TypeletsClient, env: Env): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { toolAllowedForProfile } from '../profile.js';
|
|
2
|
+
import { ok, fail } from './_shared.js';
|
|
3
|
+
export function registerWhoami(server, client, env) {
|
|
4
|
+
if (!toolAllowedForProfile('whoami', env.profile))
|
|
5
|
+
return;
|
|
6
|
+
server.registerTool('whoami', {
|
|
7
|
+
title: 'Who am I',
|
|
8
|
+
description: 'Return the authenticated caller identity (id, email, display name) for the PAT this server is using, plus the active profile. Use to confirm which account you are operating as.',
|
|
9
|
+
inputSchema: {},
|
|
10
|
+
annotations: { destructiveHint: false },
|
|
11
|
+
}, async () => {
|
|
12
|
+
try {
|
|
13
|
+
const me = await client.get('/auth/me');
|
|
14
|
+
const who = me.displayName ?? me.email ?? me.id;
|
|
15
|
+
return ok(`Authenticated as ${who} (profile: ${env.profile}).`, {
|
|
16
|
+
...me,
|
|
17
|
+
profile: env.profile,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
return fail(err);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=whoami.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/tools/whoami.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AASxC,MAAM,UAAU,cAAc,CAAC,MAAiB,EAAE,MAAsB,EAAE,GAAQ;IAChF,IAAI,CAAC,qBAAqB,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO;IAE1D,MAAM,CAAC,YAAY,CACjB,QAAQ,EACR;QACE,KAAK,EAAE,UAAU;QACjB,WAAW,EACT,kLAAkL;QACpL,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;KACxC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,GAAG,CAAa,UAAU,CAAC,CAAC;YACpD,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;YAChD,OAAO,EAAE,CAAC,oBAAoB,GAAG,cAAc,GAAG,CAAC,OAAO,IAAI,EAAE;gBAC9D,GAAG,EAAE;gBACL,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|