@adrive/mcp-server 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/LICENSE +202 -0
- package/README.md +99 -0
- package/dist/api-client.d.ts +237 -0
- package/dist/api-client.js +198 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +63 -0
- package/dist/tools/drive-activity.d.ts +13 -0
- package/dist/tools/drive-activity.js +22 -0
- package/dist/tools/drive-comment.d.ts +21 -0
- package/dist/tools/drive-comment.js +17 -0
- package/dist/tools/drive-context.d.ts +19 -0
- package/dist/tools/drive-context.js +56 -0
- package/dist/tools/drive-delete.d.ts +15 -0
- package/dist/tools/drive-delete.js +9 -0
- package/dist/tools/drive-feedback.d.ts +15 -0
- package/dist/tools/drive-feedback.js +26 -0
- package/dist/tools/drive-invite.d.ts +15 -0
- package/dist/tools/drive-invite.js +14 -0
- package/dist/tools/drive-list.d.ts +15 -0
- package/dist/tools/drive-list.js +20 -0
- package/dist/tools/drive-log-run.d.ts +25 -0
- package/dist/tools/drive-log-run.js +27 -0
- package/dist/tools/drive-org.d.ts +8 -0
- package/dist/tools/drive-org.js +30 -0
- package/dist/tools/drive-read.d.ts +15 -0
- package/dist/tools/drive-read.js +14 -0
- package/dist/tools/drive-recall.d.ts +15 -0
- package/dist/tools/drive-recall.js +29 -0
- package/dist/tools/drive-roles.d.ts +19 -0
- package/dist/tools/drive-roles.js +32 -0
- package/dist/tools/drive-search.d.ts +37 -0
- package/dist/tools/drive-search.js +47 -0
- package/dist/tools/drive-share.d.ts +23 -0
- package/dist/tools/drive-share.js +21 -0
- package/dist/tools/drive-versions.d.ts +15 -0
- package/dist/tools/drive-versions.js +13 -0
- package/dist/tools/drive-write.d.ts +19 -0
- package/dist/tools/drive-write.js +16 -0
- package/package.json +53 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// MAX_PAGES caps total to prevent unbounded loops; pass cursor explicitly if more needed.
|
|
2
|
+
export async function listAllPages(fetchPage, maxPages = 10) {
|
|
3
|
+
const all = [];
|
|
4
|
+
let cursor = null;
|
|
5
|
+
for (let i = 0; i < maxPages; i++) {
|
|
6
|
+
const page = await fetchPage(cursor);
|
|
7
|
+
all.push(...page.items);
|
|
8
|
+
if (!page.cursor)
|
|
9
|
+
return all;
|
|
10
|
+
cursor = page.cursor;
|
|
11
|
+
}
|
|
12
|
+
return all;
|
|
13
|
+
}
|
|
14
|
+
export class ApiClient {
|
|
15
|
+
baseUrl;
|
|
16
|
+
apiKey;
|
|
17
|
+
agentPlatform;
|
|
18
|
+
agentRole;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
21
|
+
this.apiKey = config.apiKey;
|
|
22
|
+
this.agentPlatform = config.agentPlatform ?? 'claude-code';
|
|
23
|
+
this.agentRole = config.agentRole ?? '';
|
|
24
|
+
}
|
|
25
|
+
headers() {
|
|
26
|
+
return {
|
|
27
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
'X-Agent-Platform': this.agentPlatform,
|
|
30
|
+
'X-Agent-Role': this.agentRole,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async rawFetch(path, init) {
|
|
34
|
+
const url = `${this.baseUrl}/v1${path}`;
|
|
35
|
+
return fetch(url, {
|
|
36
|
+
...init,
|
|
37
|
+
headers: { ...this.headers(), ...init.headers },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async request(method, path, body) {
|
|
41
|
+
const res = await this.rawFetch(path, {
|
|
42
|
+
method,
|
|
43
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
44
|
+
});
|
|
45
|
+
const json = await res.json();
|
|
46
|
+
if (!res.ok || !json.success) {
|
|
47
|
+
throw new Error(json.error || `API request failed: ${res.status}`);
|
|
48
|
+
}
|
|
49
|
+
return json.data;
|
|
50
|
+
}
|
|
51
|
+
async requestPage(method, path, body) {
|
|
52
|
+
const res = await this.rawFetch(path, {
|
|
53
|
+
method,
|
|
54
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
55
|
+
});
|
|
56
|
+
const json = await res.json();
|
|
57
|
+
if (!res.ok || !json.success) {
|
|
58
|
+
throw new Error(json.error || `API request failed: ${res.status}`);
|
|
59
|
+
}
|
|
60
|
+
return { items: (json.data ?? []), cursor: json.cursor ?? null };
|
|
61
|
+
}
|
|
62
|
+
async createWorkspace(name) {
|
|
63
|
+
return this.request('POST', '/workspaces', { name });
|
|
64
|
+
}
|
|
65
|
+
async writeFile(workspaceId, path, content, message) {
|
|
66
|
+
return this.request('POST', `/workspaces/${workspaceId}/files`, { path, content, message });
|
|
67
|
+
}
|
|
68
|
+
async readFile(workspaceId, fileId) {
|
|
69
|
+
return this.request('GET', `/workspaces/${workspaceId}/files/${fileId}?content=true`);
|
|
70
|
+
}
|
|
71
|
+
async listFilesPage(workspaceId, prefix, cursor) {
|
|
72
|
+
const params = new URLSearchParams();
|
|
73
|
+
if (prefix)
|
|
74
|
+
params.set('prefix', prefix);
|
|
75
|
+
if (cursor)
|
|
76
|
+
params.set('cursor', cursor);
|
|
77
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
78
|
+
return this.requestPage('GET', `/workspaces/${workspaceId}/files${query}`);
|
|
79
|
+
}
|
|
80
|
+
async listAllFiles(workspaceId, prefix, maxPages = 10) {
|
|
81
|
+
return listAllPages((cursor) => this.listFilesPage(workspaceId, prefix, cursor), maxPages);
|
|
82
|
+
}
|
|
83
|
+
async deleteFile(workspaceId, fileId) {
|
|
84
|
+
return this.request('DELETE', `/workspaces/${workspaceId}/files/${fileId}`);
|
|
85
|
+
}
|
|
86
|
+
async getVersionsPage(workspaceId, fileId, cursor) {
|
|
87
|
+
const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';
|
|
88
|
+
return this.requestPage('GET', `/workspaces/${workspaceId}/files/${fileId}/versions${query}`);
|
|
89
|
+
}
|
|
90
|
+
async getAllVersions(workspaceId, fileId, maxPages = 10) {
|
|
91
|
+
return listAllPages((cursor) => this.getVersionsPage(workspaceId, fileId, cursor), maxPages);
|
|
92
|
+
}
|
|
93
|
+
async createShareLink(workspaceId, permission, fileId, expiresIn, maxUses, email) {
|
|
94
|
+
return this.request('POST', `/workspaces/${workspaceId}/share`, {
|
|
95
|
+
permission,
|
|
96
|
+
file_id: fileId,
|
|
97
|
+
expires_in: expiresIn,
|
|
98
|
+
max_uses: maxUses,
|
|
99
|
+
email,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async addComment(workspaceId, fileId, body, authorName, lineStart, lineEnd) {
|
|
103
|
+
return this.request('POST', `/workspaces/${workspaceId}/files/${fileId}/comments`, {
|
|
104
|
+
body, author_name: authorName,
|
|
105
|
+
line_start: lineStart, line_end: lineEnd,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async getCommentsPage(workspaceId, fileId, cursor) {
|
|
109
|
+
const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';
|
|
110
|
+
return this.requestPage('GET', `/workspaces/${workspaceId}/files/${fileId}/comments${query}`);
|
|
111
|
+
}
|
|
112
|
+
async getAllComments(workspaceId, fileId, maxPages = 10) {
|
|
113
|
+
return listAllPages((cursor) => this.getCommentsPage(workspaceId, fileId, cursor), maxPages);
|
|
114
|
+
}
|
|
115
|
+
async getActivityPage(workspaceId, cursor) {
|
|
116
|
+
const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';
|
|
117
|
+
return this.requestPage('GET', `/workspaces/${workspaceId}/activity${query}`);
|
|
118
|
+
}
|
|
119
|
+
async getAllActivity(workspaceId, maxPages = 10) {
|
|
120
|
+
return listAllPages((cursor) => this.getActivityPage(workspaceId, cursor), maxPages);
|
|
121
|
+
}
|
|
122
|
+
async assignRole(workspaceId, email, permission) {
|
|
123
|
+
return this.request('POST', `/workspaces/${workspaceId}/roles`, { email, permission });
|
|
124
|
+
}
|
|
125
|
+
async getRolesPage(workspaceId, cursor) {
|
|
126
|
+
const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';
|
|
127
|
+
return this.requestPage('GET', `/workspaces/${workspaceId}/roles${query}`);
|
|
128
|
+
}
|
|
129
|
+
async getAllRoles(workspaceId, maxPages = 10) {
|
|
130
|
+
return listAllPages((cursor) => this.getRolesPage(workspaceId, cursor), maxPages);
|
|
131
|
+
}
|
|
132
|
+
async removeRole(workspaceId, email) {
|
|
133
|
+
return this.request('DELETE', `/workspaces/${workspaceId}/roles/${encodeURIComponent(email)}`);
|
|
134
|
+
}
|
|
135
|
+
async inviteMember(email, role = 'member') {
|
|
136
|
+
return this.request('POST', '/org/invite', { email, role });
|
|
137
|
+
}
|
|
138
|
+
async getOrg(maxPages = 10) {
|
|
139
|
+
// Org returns agents + members each with its own cursor; loop both to exhaustion.
|
|
140
|
+
const first = await this.fetchOrgPage(undefined, undefined);
|
|
141
|
+
const agents = [...first.agents];
|
|
142
|
+
const members = [...first.members];
|
|
143
|
+
let agentsCursor = first.agents_cursor ?? undefined;
|
|
144
|
+
let membersCursor = first.members_cursor ?? undefined;
|
|
145
|
+
for (let i = 1; i < maxPages && agentsCursor; i++) {
|
|
146
|
+
const page = await this.fetchOrgPage(agentsCursor, undefined);
|
|
147
|
+
agents.push(...page.agents);
|
|
148
|
+
agentsCursor = page.agents_cursor ?? undefined;
|
|
149
|
+
}
|
|
150
|
+
for (let i = 1; i < maxPages && membersCursor; i++) {
|
|
151
|
+
const page = await this.fetchOrgPage(undefined, membersCursor);
|
|
152
|
+
members.push(...page.members);
|
|
153
|
+
membersCursor = page.members_cursor ?? undefined;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
...first.org,
|
|
157
|
+
agents,
|
|
158
|
+
members,
|
|
159
|
+
usage: first.usage,
|
|
160
|
+
quota: first.quota,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async fetchOrgPage(agentsCursor, membersCursor) {
|
|
164
|
+
const params = new URLSearchParams();
|
|
165
|
+
if (agentsCursor)
|
|
166
|
+
params.set('agents_cursor', agentsCursor);
|
|
167
|
+
if (membersCursor)
|
|
168
|
+
params.set('members_cursor', membersCursor);
|
|
169
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
170
|
+
const res = await this.rawFetch(`/org${query}`, { method: 'GET' });
|
|
171
|
+
const json = await res.json();
|
|
172
|
+
if (!res.ok || !json.success || !json.data) {
|
|
173
|
+
throw new Error(json.error || `API request failed: ${res.status}`);
|
|
174
|
+
}
|
|
175
|
+
const { agents, members, usage, quota, ...org } = json.data;
|
|
176
|
+
return {
|
|
177
|
+
org,
|
|
178
|
+
agents,
|
|
179
|
+
members,
|
|
180
|
+
usage,
|
|
181
|
+
quota,
|
|
182
|
+
agents_cursor: json.agents_cursor ?? null,
|
|
183
|
+
members_cursor: json.members_cursor ?? null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async getBillingPlan() {
|
|
187
|
+
return this.request('GET', '/billing/plan');
|
|
188
|
+
}
|
|
189
|
+
async search(body) {
|
|
190
|
+
return this.request('POST', '/search', body);
|
|
191
|
+
}
|
|
192
|
+
async context(body) {
|
|
193
|
+
return this.request('POST', '/context', body);
|
|
194
|
+
}
|
|
195
|
+
async logRun(body) {
|
|
196
|
+
return this.request('POST', '/runs', body);
|
|
197
|
+
}
|
|
198
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { ApiClient } from './api-client.js';
|
|
5
|
+
import { driveWriteSchema, driveWrite } from './tools/drive-write.js';
|
|
6
|
+
import { driveReadSchema, driveRead } from './tools/drive-read.js';
|
|
7
|
+
import { driveListSchema, driveList } from './tools/drive-list.js';
|
|
8
|
+
import { driveShareSchema, driveShare } from './tools/drive-share.js';
|
|
9
|
+
import { driveCommentSchema, driveComment } from './tools/drive-comment.js';
|
|
10
|
+
import { driveFeedbackSchema, driveFeedback } from './tools/drive-feedback.js';
|
|
11
|
+
import { driveActivitySchema, driveActivity } from './tools/drive-activity.js';
|
|
12
|
+
import { driveInviteSchema, driveInvite } from './tools/drive-invite.js';
|
|
13
|
+
import { driveRolesSchema, driveRoles } from './tools/drive-roles.js';
|
|
14
|
+
import { driveOrgSchema, driveOrg } from './tools/drive-org.js';
|
|
15
|
+
import { driveDeleteSchema, driveDelete } from './tools/drive-delete.js';
|
|
16
|
+
import { driveVersionsSchema, driveVersions } from './tools/drive-versions.js';
|
|
17
|
+
import { driveSearchSchema, driveSearch } from './tools/drive-search.js';
|
|
18
|
+
import { driveRecallSchema, driveRecall } from './tools/drive-recall.js';
|
|
19
|
+
import { driveLogRunSchema, driveLogRun } from './tools/drive-log-run.js';
|
|
20
|
+
import { driveContextSchema, driveContext } from './tools/drive-context.js';
|
|
21
|
+
const BASE_URL = process.env.AGENTDRIVE_API_URL || 'https://app.agentdrive.sh';
|
|
22
|
+
const API_KEY = process.env.AGENTDRIVE_API_KEY;
|
|
23
|
+
const AGENT_PLATFORM = process.env.AGENTDRIVE_AGENT_PLATFORM || 'claude-code';
|
|
24
|
+
const AGENT_ROLE = process.env.AGENTDRIVE_AGENT_ROLE || '';
|
|
25
|
+
if (!API_KEY) {
|
|
26
|
+
console.error('AGENTDRIVE_API_KEY environment variable is required');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const client = new ApiClient({
|
|
30
|
+
baseUrl: BASE_URL,
|
|
31
|
+
apiKey: API_KEY,
|
|
32
|
+
agentPlatform: AGENT_PLATFORM,
|
|
33
|
+
agentRole: AGENT_ROLE,
|
|
34
|
+
});
|
|
35
|
+
const server = new McpServer({
|
|
36
|
+
name: 'agentdrive',
|
|
37
|
+
version: '0.3.0',
|
|
38
|
+
description: 'AgentDrive — the persistent workspace your agents share across Claude Code, Codex, Cursor, and LangGraph. Files, semantic search, run logging, and cross-platform memory.',
|
|
39
|
+
});
|
|
40
|
+
server.tool('drive_write', 'Write a markdown file to a workspace. Creates or updates the file with automatic versioning.', driveWriteSchema, (args) => driveWrite(client, args));
|
|
41
|
+
server.tool('drive_read', 'Read a file\'s content from a workspace.', driveReadSchema, (args) => driveRead(client, args));
|
|
42
|
+
server.tool('drive_list', 'List files in a workspace, optionally filtered by path prefix.', driveListSchema, (args) => driveList(client, args));
|
|
43
|
+
server.tool('drive_share', 'Generate a share link for a workspace or specific file. Optionally email-gate the link so only a specific person can access it. Humans open this link to view, comment, or edit files.', driveShareSchema, (args) => driveShare(client, args));
|
|
44
|
+
server.tool('drive_comment', 'Add a comment to a file.', driveCommentSchema, (args) => driveComment(client, args));
|
|
45
|
+
server.tool('drive_feedback', 'Read human comments on a file. Use this to check if humans have left feedback.', driveFeedbackSchema, (args) => driveFeedback(client, args));
|
|
46
|
+
server.tool('drive_activity', 'Get recent activity in a workspace (file writes, comments, shares).', driveActivitySchema, (args) => driveActivity(client, args));
|
|
47
|
+
server.tool('drive_invite', 'Invite a human to your organization by email. They will receive a verification code to join.', driveInviteSchema, (args) => driveInvite(client, args));
|
|
48
|
+
server.tool('drive_roles', 'Manage per-workspace role delegation. Assign, list, or remove access for specific emails (view/comment/edit).', driveRolesSchema, (args) => driveRoles(client, args));
|
|
49
|
+
server.tool('drive_org', 'View your organization details: agents, members, plan, and usage.', driveOrgSchema, (args) => driveOrg(client, args));
|
|
50
|
+
server.tool('drive_delete', 'Delete a file from a workspace.', driveDeleteSchema, (args) => driveDelete(client, args));
|
|
51
|
+
server.tool('drive_versions', 'View version history for a file in a workspace.', driveVersionsSchema, (args) => driveVersions(client, args));
|
|
52
|
+
server.tool('drive_search', "Semantic search across your org's artifacts in AgentDrive. Returns the top-N most relevant files. Use this BEFORE writing similar artifacts to avoid duplication and surface prior context.", driveSearchSchema, (args) => driveSearch(client, args));
|
|
53
|
+
server.tool('drive_recall', 'Recall persistent facts and preferences your agents have written. In v1 this returns semantic search results across artifacts; v1.5 will narrow to auto-extracted facts (preferences, decisions, key entities).', driveRecallSchema, (args) => driveRecall(client, args));
|
|
54
|
+
server.tool('drive_log_run', "Log an agent run to AgentDrive's provenance store. Captures task, status, referenced/produced artifacts, and cross-platform identity (which agent on which tool). Used for retrospective context retrieval and team-wide audit.", driveLogRunSchema, (args) => driveLogRun(client, args));
|
|
55
|
+
server.tool('drive_context', 'Auto-pull the most relevant artifacts from prior agent sessions for the task you are about to do. Combines semantic relevance with recency. Call this at the START of any non-trivial task to ground the agent in prior context — replaces manual copy-paste of artifacts between sessions.', driveContextSchema, (args) => driveContext(client, args));
|
|
56
|
+
async function main() {
|
|
57
|
+
const transport = new StdioServerTransport();
|
|
58
|
+
await server.connect(transport);
|
|
59
|
+
}
|
|
60
|
+
main().catch((err) => {
|
|
61
|
+
console.error('Fatal error:', err);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveActivitySchema: {
|
|
4
|
+
workspace_id: z.ZodString;
|
|
5
|
+
};
|
|
6
|
+
export declare function driveActivity(client: ApiClient, args: {
|
|
7
|
+
workspace_id: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
content: {
|
|
10
|
+
type: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveActivitySchema = {
|
|
3
|
+
workspace_id: z.string().describe('Workspace ID'),
|
|
4
|
+
};
|
|
5
|
+
export async function driveActivity(client, args) {
|
|
6
|
+
const events = await client.getAllActivity(args.workspace_id);
|
|
7
|
+
if (events.length === 0) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: 'text', text: 'No recent activity.' }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const formatted = events.map(e => {
|
|
13
|
+
const path = e.target_path ? ` → ${e.target_path}` : '';
|
|
14
|
+
return `[${e.actor_type}] ${e.actor_id}: ${e.action}${path} — ${e.created_at}`;
|
|
15
|
+
}).join('\n');
|
|
16
|
+
return {
|
|
17
|
+
content: [{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: `Recent activity (${events.length}):\n${formatted}`,
|
|
20
|
+
}],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveCommentSchema: {
|
|
4
|
+
workspace_id: z.ZodString;
|
|
5
|
+
file_id: z.ZodString;
|
|
6
|
+
body: z.ZodString;
|
|
7
|
+
line_start: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
line_end: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
};
|
|
10
|
+
export declare function driveComment(client: ApiClient, args: {
|
|
11
|
+
workspace_id: string;
|
|
12
|
+
file_id: string;
|
|
13
|
+
body: string;
|
|
14
|
+
line_start?: number;
|
|
15
|
+
line_end?: number;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
content: {
|
|
18
|
+
type: "text";
|
|
19
|
+
text: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveCommentSchema = {
|
|
3
|
+
workspace_id: z.string().describe('Workspace ID'),
|
|
4
|
+
file_id: z.string().describe('File ID to comment on'),
|
|
5
|
+
body: z.string().describe('Comment text'),
|
|
6
|
+
line_start: z.number().optional().describe('Start line number (optional)'),
|
|
7
|
+
line_end: z.number().optional().describe('End line number (optional)'),
|
|
8
|
+
};
|
|
9
|
+
export async function driveComment(client, args) {
|
|
10
|
+
const comment = await client.addComment(args.workspace_id, args.file_id, args.body, undefined, args.line_start, args.line_end);
|
|
11
|
+
return {
|
|
12
|
+
content: [{
|
|
13
|
+
type: 'text',
|
|
14
|
+
text: `Comment added (id: ${comment.id})`,
|
|
15
|
+
}],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveContextSchema: {
|
|
4
|
+
task: z.ZodString;
|
|
5
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
6
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
include_facts: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
+
};
|
|
9
|
+
export declare function driveContext(client: ApiClient, args: {
|
|
10
|
+
task: string;
|
|
11
|
+
workspace_id?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
include_facts?: boolean;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveContextSchema = {
|
|
3
|
+
task: z.string().min(1).max(2000).describe('What you are about to do — used to rank prior artifacts by relevance'),
|
|
4
|
+
workspace_id: z.string().optional().describe('Scope to a single workspace; omit for org-wide context'),
|
|
5
|
+
limit: z.number().int().positive().max(20).optional().describe('Max artifacts to return (default 5, max 20)'),
|
|
6
|
+
include_facts: z.boolean().optional().describe('Include extracted facts alongside artifacts (v1.5+)'),
|
|
7
|
+
};
|
|
8
|
+
export async function driveContext(client, args) {
|
|
9
|
+
const { artifacts } = await client.context({
|
|
10
|
+
task: args.task,
|
|
11
|
+
workspace_id: args.workspace_id,
|
|
12
|
+
limit: args.limit ?? 5,
|
|
13
|
+
include_facts: args.include_facts ?? false,
|
|
14
|
+
});
|
|
15
|
+
if (artifacts.length === 0) {
|
|
16
|
+
return { content: [{ type: 'text', text: 'No prior context found.' }] };
|
|
17
|
+
}
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
return {
|
|
20
|
+
content: [{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: artifacts.map((a) => formatArtifact(a, now)).join('\n\n---\n\n'),
|
|
23
|
+
}],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function formatArtifact(a, now) {
|
|
27
|
+
const snippet = truncate(a.snippet ?? '', 500);
|
|
28
|
+
const rel = relativeTime(now - a.created_at);
|
|
29
|
+
return `${a.path} (by ${a.agent_id}@${a.agent_platform}, ${rel})\n${snippet}`;
|
|
30
|
+
}
|
|
31
|
+
function truncate(s, n) {
|
|
32
|
+
if (s.length <= n)
|
|
33
|
+
return s;
|
|
34
|
+
return s.slice(0, n - 1) + '...';
|
|
35
|
+
}
|
|
36
|
+
function relativeTime(ms) {
|
|
37
|
+
if (ms < 0)
|
|
38
|
+
return 'just now';
|
|
39
|
+
const sec = Math.floor(ms / 1000);
|
|
40
|
+
if (sec < 60)
|
|
41
|
+
return `${sec}s ago`;
|
|
42
|
+
const min = Math.floor(sec / 60);
|
|
43
|
+
if (min < 60)
|
|
44
|
+
return `${min}m ago`;
|
|
45
|
+
const hr = Math.floor(min / 60);
|
|
46
|
+
if (hr < 24)
|
|
47
|
+
return `${hr}h ago`;
|
|
48
|
+
const day = Math.floor(hr / 24);
|
|
49
|
+
if (day < 30)
|
|
50
|
+
return `${day}d ago`;
|
|
51
|
+
const mo = Math.floor(day / 30);
|
|
52
|
+
if (mo < 12)
|
|
53
|
+
return `${mo}mo ago`;
|
|
54
|
+
const yr = Math.floor(mo / 12);
|
|
55
|
+
return `${yr}y ago`;
|
|
56
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveDeleteSchema: {
|
|
4
|
+
workspace_id: z.ZodString;
|
|
5
|
+
file_id: z.ZodString;
|
|
6
|
+
};
|
|
7
|
+
export declare function driveDelete(client: ApiClient, args: {
|
|
8
|
+
workspace_id: string;
|
|
9
|
+
file_id: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveDeleteSchema = {
|
|
3
|
+
workspace_id: z.string().describe('Workspace ID'),
|
|
4
|
+
file_id: z.string().describe('File ID to delete'),
|
|
5
|
+
};
|
|
6
|
+
export async function driveDelete(client, args) {
|
|
7
|
+
const result = await client.deleteFile(args.workspace_id, args.file_id);
|
|
8
|
+
return { content: [{ type: 'text', text: `Deleted file ${args.file_id} from workspace ${args.workspace_id}` }] };
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveFeedbackSchema: {
|
|
4
|
+
workspace_id: z.ZodString;
|
|
5
|
+
file_id: z.ZodString;
|
|
6
|
+
};
|
|
7
|
+
export declare function driveFeedback(client: ApiClient, args: {
|
|
8
|
+
workspace_id: string;
|
|
9
|
+
file_id: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveFeedbackSchema = {
|
|
3
|
+
workspace_id: z.string().describe('Workspace ID'),
|
|
4
|
+
file_id: z.string().describe('File ID to read comments from'),
|
|
5
|
+
};
|
|
6
|
+
export async function driveFeedback(client, args) {
|
|
7
|
+
const comments = await client.getAllComments(args.workspace_id, args.file_id);
|
|
8
|
+
if (comments.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: 'text', text: 'No comments on this file.' }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const formatted = comments.map(c => {
|
|
14
|
+
const author = c.author_name || c.author_id;
|
|
15
|
+
const lines = c.line_start
|
|
16
|
+
? ` (lines ${c.line_start}${c.line_end ? `-${c.line_end}` : ''})`
|
|
17
|
+
: '';
|
|
18
|
+
return `[${c.author_type}] ${author}${lines} — ${c.created_at}:\n${c.body}`;
|
|
19
|
+
}).join('\n\n---\n\n');
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: `Comments (${comments.length}):\n\n${formatted}`,
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveInviteSchema: {
|
|
4
|
+
email: z.ZodString;
|
|
5
|
+
role: z.ZodDefault<z.ZodEnum<["owner", "admin", "member"]>>;
|
|
6
|
+
};
|
|
7
|
+
export declare function driveInvite(client: ApiClient, args: {
|
|
8
|
+
email: string;
|
|
9
|
+
role: 'owner' | 'admin' | 'member';
|
|
10
|
+
}): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveInviteSchema = {
|
|
3
|
+
email: z.string().describe('Email address of the human to invite to your organization'),
|
|
4
|
+
role: z.enum(['owner', 'admin', 'member']).default('member').describe('Role in the organization (default: member)'),
|
|
5
|
+
};
|
|
6
|
+
export async function driveInvite(client, args) {
|
|
7
|
+
const member = await client.inviteMember(args.email, args.role);
|
|
8
|
+
return {
|
|
9
|
+
content: [{
|
|
10
|
+
type: 'text',
|
|
11
|
+
text: `Invited ${args.email} as ${args.role}. They'll receive an email with a verification code to join the organization.`,
|
|
12
|
+
}],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveListSchema: {
|
|
4
|
+
workspace_id: z.ZodString;
|
|
5
|
+
prefix: z.ZodOptional<z.ZodString>;
|
|
6
|
+
};
|
|
7
|
+
export declare function driveList(client: ApiClient, args: {
|
|
8
|
+
workspace_id: string;
|
|
9
|
+
prefix?: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveListSchema = {
|
|
3
|
+
workspace_id: z.string().describe('Workspace ID'),
|
|
4
|
+
prefix: z.string().optional().describe('Path prefix filter (e.g. "notes/")'),
|
|
5
|
+
};
|
|
6
|
+
export async function driveList(client, args) {
|
|
7
|
+
const files = await client.listAllFiles(args.workspace_id, args.prefix);
|
|
8
|
+
if (files.length === 0) {
|
|
9
|
+
return {
|
|
10
|
+
content: [{ type: 'text', text: 'No files found.' }],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const listing = files.map(f => `- ${f.path} (v${f.version}, ${f.size} bytes, updated: ${f.updated_at})`).join('\n');
|
|
14
|
+
return {
|
|
15
|
+
content: [{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: `Files in workspace:\n${listing}`,
|
|
18
|
+
}],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ApiClient } from '../api-client.js';
|
|
3
|
+
export declare const driveLogRunSchema: {
|
|
4
|
+
task: z.ZodString;
|
|
5
|
+
status: z.ZodOptional<z.ZodEnum<["success", "failure", "partial", "in_progress"]>>;
|
|
6
|
+
run_id: z.ZodOptional<z.ZodString>;
|
|
7
|
+
artifacts_referenced: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
8
|
+
artifacts_produced: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
|
+
duration_ms: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
11
|
+
};
|
|
12
|
+
export declare function driveLogRun(client: ApiClient, args: {
|
|
13
|
+
task: string;
|
|
14
|
+
status?: 'success' | 'failure' | 'partial' | 'in_progress';
|
|
15
|
+
run_id?: string;
|
|
16
|
+
artifacts_referenced?: string[];
|
|
17
|
+
artifacts_produced?: string[];
|
|
18
|
+
duration_ms?: number;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
content: {
|
|
22
|
+
type: "text";
|
|
23
|
+
text: string;
|
|
24
|
+
}[];
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const driveLogRunSchema = {
|
|
3
|
+
task: z.string().min(1).describe('What the agent did or is doing (free text)'),
|
|
4
|
+
status: z.enum(['success', 'failure', 'partial', 'in_progress']).optional().describe('Run outcome (default success)'),
|
|
5
|
+
run_id: z.string().optional().describe('Existing run ID to update; omit to let server allocate'),
|
|
6
|
+
artifacts_referenced: z.array(z.string()).optional().describe('Paths of artifacts the run read'),
|
|
7
|
+
artifacts_produced: z.array(z.string()).optional().describe('Paths of artifacts the run wrote'),
|
|
8
|
+
duration_ms: z.number().int().nonnegative().optional().describe('Wall-clock duration in milliseconds'),
|
|
9
|
+
metadata: z.record(z.unknown()).optional().describe('Free-form key/value metadata'),
|
|
10
|
+
};
|
|
11
|
+
export async function driveLogRun(client, args) {
|
|
12
|
+
const result = await client.logRun({
|
|
13
|
+
task: args.task,
|
|
14
|
+
status: args.status ?? 'success',
|
|
15
|
+
run_id: args.run_id,
|
|
16
|
+
artifacts_referenced: args.artifacts_referenced,
|
|
17
|
+
artifacts_produced: args.artifacts_produced,
|
|
18
|
+
duration_ms: args.duration_ms,
|
|
19
|
+
metadata: args.metadata,
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
content: [{
|
|
23
|
+
type: 'text',
|
|
24
|
+
text: `Run logged: ${result.run_id} (${new Date(result.created_at).toISOString()})`,
|
|
25
|
+
}],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const driveOrgSchema = {};
|
|
2
|
+
export async function driveOrg(client, _args) {
|
|
3
|
+
const org = await client.getOrg();
|
|
4
|
+
const agentList = org.agents.map(a => ` - ${a.agent_name || a.agent_id}${a.agent_email ? ` (${a.agent_email})` : ''}`).join('\n');
|
|
5
|
+
const memberList = org.members.map(m => ` - ${m.email} (${m.role})`).join('\n');
|
|
6
|
+
const text = [
|
|
7
|
+
`Organization: ${org.name} (${org.id})`,
|
|
8
|
+
`Plan: ${org.plan}`,
|
|
9
|
+
'',
|
|
10
|
+
`Agents (${org.agents.length}/${org.quota.max_agents}):`,
|
|
11
|
+
agentList || ' (none)',
|
|
12
|
+
'',
|
|
13
|
+
`Members (${org.members.length}/${org.quota.max_human_seats}):`,
|
|
14
|
+
memberList || ' (none)',
|
|
15
|
+
'',
|
|
16
|
+
`Usage:`,
|
|
17
|
+
` Storage: ${formatBytes(org.usage.storage_bytes)} / ${formatBytes(org.quota.storage_bytes)}`,
|
|
18
|
+
` Workspaces: ${org.usage.workspace_count} / ${org.quota.max_workspaces}`,
|
|
19
|
+
].join('\n');
|
|
20
|
+
return { content: [{ type: 'text', text }] };
|
|
21
|
+
}
|
|
22
|
+
function formatBytes(bytes) {
|
|
23
|
+
if (bytes < 1024)
|
|
24
|
+
return `${bytes} B`;
|
|
25
|
+
if (bytes < 1024 * 1024)
|
|
26
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
27
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
28
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
29
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
30
|
+
}
|