@slates-integrations/anthropic 0.2.0-rc.5

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/src/spec.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { SlateSpecification } from 'slates';
2
+ import { auth } from './auth';
3
+ import { config } from './config';
4
+
5
+ export let spec = SlateSpecification.create({
6
+ key: 'anthropic',
7
+ name: 'Anthropic',
8
+ description:
9
+ 'AI research company providing access to the Claude family of large language models via a REST API.',
10
+ metadata: {},
11
+ config,
12
+ auth
13
+ });
@@ -0,0 +1,74 @@
1
+ import { SlateTool } from 'slates';
2
+ import { AnthropicClient } from '../lib/client';
3
+ import { spec } from '../spec';
4
+ import { z } from 'zod';
5
+
6
+ let messageSchema = z.object({
7
+ role: z.enum(['user', 'assistant']).describe('Role of the message sender'),
8
+ content: z
9
+ .union([z.string(), z.array(z.record(z.string(), z.unknown()))])
10
+ .describe('Message content')
11
+ });
12
+
13
+ export let countTokens = SlateTool.create(spec, {
14
+ name: 'Count Tokens',
15
+ key: 'count_tokens',
16
+ description: `Count the number of tokens in a message without sending it. Useful for estimating costs, managing context window limits, and planning prompt strategies before making actual API calls.`,
17
+ tags: {
18
+ destructive: false,
19
+ readOnly: true
20
+ }
21
+ })
22
+ .input(
23
+ z.object({
24
+ model: z.string().describe('Claude model ID to use for token counting'),
25
+ messages: z.array(messageSchema).describe('Messages to count tokens for'),
26
+ system: z.string().optional().describe('System prompt to include in the count'),
27
+ tools: z
28
+ .array(z.record(z.string(), z.unknown()))
29
+ .optional()
30
+ .describe('Tool definitions to include in the count'),
31
+ thinking: z
32
+ .record(z.string(), z.unknown())
33
+ .optional()
34
+ .describe('Extended thinking configuration to include in the count'),
35
+ betaHeaders: z
36
+ .array(z.string())
37
+ .optional()
38
+ .describe('Anthropic beta headers to send with this request')
39
+ })
40
+ )
41
+ .output(
42
+ z.object({
43
+ inputTokens: z
44
+ .number()
45
+ .describe('Total number of tokens across messages, system prompt, and tools')
46
+ })
47
+ )
48
+ .handleInvocation(async ctx => {
49
+ let client = new AnthropicClient({
50
+ token: ctx.auth.token,
51
+ apiVersion: ctx.config.apiVersion
52
+ });
53
+
54
+ let messages = ctx.input.messages as Array<{
55
+ role: 'user' | 'assistant';
56
+ content: string | Array<Record<string, unknown>>;
57
+ }>;
58
+ let result = await client.countTokens({
59
+ model: ctx.input.model,
60
+ messages,
61
+ system: ctx.input.system,
62
+ tools: ctx.input.tools,
63
+ thinking: ctx.input.thinking,
64
+ betaHeaders: ctx.input.betaHeaders
65
+ });
66
+
67
+ return {
68
+ output: {
69
+ inputTokens: result.inputTokens
70
+ },
71
+ message: `Token count for model **${ctx.input.model}**: **${result.inputTokens}** input tokens.`
72
+ };
73
+ })
74
+ .build();
@@ -0,0 +1,36 @@
1
+ import { SlateTool } from 'slates';
2
+ import { AnthropicClient } from '../lib/client';
3
+ import { spec } from '../spec';
4
+ import { z } from 'zod';
5
+
6
+ export let getOrganization = SlateTool.create(spec, {
7
+ name: 'Get Organization',
8
+ key: 'get_organization',
9
+ description: `Retrieve information about the organization associated with the current Admin API key. Useful for determining which organization a key belongs to and getting organization details.
10
+ Requires an Admin API key (sk-ant-admin...).`,
11
+ constraints: ['Requires an Admin API key (sk-ant-admin...).'],
12
+ tags: {
13
+ destructive: false,
14
+ readOnly: true
15
+ }
16
+ })
17
+ .input(z.object({}))
18
+ .output(
19
+ z.object({
20
+ organization: z.record(z.string(), z.unknown()).describe('Organization details')
21
+ })
22
+ )
23
+ .handleInvocation(async ctx => {
24
+ let client = new AnthropicClient({
25
+ token: ctx.auth.token,
26
+ apiVersion: ctx.config.apiVersion
27
+ });
28
+
29
+ let organization = await client.getOrganization();
30
+
31
+ return {
32
+ output: { organization },
33
+ message: `Retrieved organization info: **${(organization as Record<string, unknown>).name || organization.id || 'unknown'}**.`
34
+ };
35
+ })
36
+ .build();
@@ -0,0 +1,141 @@
1
+ import { SlateTool } from 'slates';
2
+ import { AnthropicClient } from '../lib/client';
3
+ import { anthropicServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ let usageGroupBySchema = z.enum([
8
+ 'api_key_id',
9
+ 'workspace_id',
10
+ 'model',
11
+ 'service_tier',
12
+ 'context_window',
13
+ 'description'
14
+ ]);
15
+
16
+ let costGroupBy = new Set(['workspace_id', 'description']);
17
+ let messagesUsageGroupBy = new Set([
18
+ 'api_key_id',
19
+ 'workspace_id',
20
+ 'model',
21
+ 'service_tier',
22
+ 'context_window'
23
+ ]);
24
+
25
+ let ensureValidGroupBy = (reportType: 'messages_usage' | 'cost', groupBy?: string[]) => {
26
+ let allowed = reportType === 'cost' ? costGroupBy : messagesUsageGroupBy;
27
+ let invalid = (groupBy ?? []).filter(group => !allowed.has(group));
28
+ if (invalid.length > 0) {
29
+ throw anthropicServiceError(
30
+ `${invalid.join(', ')} cannot be used as groupBy for "${reportType}"`
31
+ );
32
+ }
33
+ };
34
+
35
+ export let getUsageReport = SlateTool.create(spec, {
36
+ name: 'Get Usage Report',
37
+ key: 'get_usage_report',
38
+ description: `Retrieve Anthropic Admin API usage and cost reports for an organization. Use message usage reports for token and server-tool usage, and cost reports for USD spend attribution.`,
39
+ instructions: [
40
+ 'Requires an Admin API key (sk-ant-admin...).',
41
+ 'startingAt and endingAt must be RFC 3339 timestamps; buckets are snapped to UTC boundaries by Anthropic.',
42
+ 'For messages_usage, groupBy supports api_key_id, workspace_id, model, service_tier, and context_window.',
43
+ 'For cost, groupBy supports workspace_id and description.'
44
+ ],
45
+ constraints: ['Requires an Admin API key (sk-ant-admin...).'],
46
+ tags: {
47
+ destructive: false,
48
+ readOnly: true
49
+ }
50
+ })
51
+ .input(
52
+ z.object({
53
+ reportType: z.enum(['messages_usage', 'cost']).describe('Report endpoint to query'),
54
+ startingAt: z
55
+ .string()
56
+ .describe('RFC 3339 timestamp; buckets starting on or after this time are returned'),
57
+ endingAt: z
58
+ .string()
59
+ .optional()
60
+ .describe('RFC 3339 timestamp; buckets ending before this time are returned'),
61
+ bucketWidth: z
62
+ .enum(['1m', '1h', '1d'])
63
+ .optional()
64
+ .describe('Bucket width for messages_usage reports'),
65
+ groupBy: z
66
+ .array(usageGroupBySchema)
67
+ .optional()
68
+ .describe('Dimensions to group report rows by'),
69
+ apiKeyIds: z
70
+ .array(z.string())
71
+ .optional()
72
+ .describe('Filter messages_usage by API key IDs'),
73
+ workspaceIds: z
74
+ .array(z.string())
75
+ .optional()
76
+ .describe('Filter messages_usage by workspace IDs'),
77
+ models: z.array(z.string()).optional().describe('Filter messages_usage by model IDs'),
78
+ serviceTiers: z
79
+ .array(z.enum(['standard', 'batch', 'priority']))
80
+ .optional()
81
+ .describe('Filter messages_usage by service tiers'),
82
+ contextWindows: z
83
+ .array(z.enum(['0-200k', '200k-1M']))
84
+ .optional()
85
+ .describe('Filter messages_usage by context window'),
86
+ limit: z.number().optional().describe('Maximum number of time buckets to return'),
87
+ page: z.string().optional().describe('Pagination token returned as nextPage')
88
+ })
89
+ )
90
+ .output(
91
+ z.object({
92
+ data: z.array(z.record(z.string(), z.unknown())).describe('Report buckets'),
93
+ hasMore: z.boolean().describe('Whether more report data is available'),
94
+ nextPage: z.string().optional().describe('Pagination token for a follow-up request')
95
+ })
96
+ )
97
+ .handleInvocation(async ctx => {
98
+ ensureValidGroupBy(ctx.input.reportType, ctx.input.groupBy);
99
+
100
+ if (ctx.input.reportType === 'cost' && ctx.input.bucketWidth !== undefined) {
101
+ throw anthropicServiceError('bucketWidth is only supported for messages_usage reports');
102
+ }
103
+
104
+ let client = new AnthropicClient({
105
+ token: ctx.auth.token,
106
+ apiVersion: ctx.config.apiVersion
107
+ });
108
+
109
+ let result =
110
+ ctx.input.reportType === 'messages_usage'
111
+ ? await client.getMessagesUsageReport({
112
+ startingAt: ctx.input.startingAt,
113
+ endingAt: ctx.input.endingAt,
114
+ bucketWidth: ctx.input.bucketWidth,
115
+ groupBy: ctx.input.groupBy,
116
+ apiKeyIds: ctx.input.apiKeyIds,
117
+ workspaceIds: ctx.input.workspaceIds,
118
+ models: ctx.input.models,
119
+ serviceTiers: ctx.input.serviceTiers,
120
+ contextWindows: ctx.input.contextWindows,
121
+ limit: ctx.input.limit,
122
+ page: ctx.input.page
123
+ })
124
+ : await client.getCostReport({
125
+ startingAt: ctx.input.startingAt,
126
+ endingAt: ctx.input.endingAt,
127
+ groupBy: ctx.input.groupBy,
128
+ limit: ctx.input.limit,
129
+ page: ctx.input.page
130
+ });
131
+
132
+ return {
133
+ output: {
134
+ data: result.data,
135
+ hasMore: result.hasMore,
136
+ nextPage: result.nextPage ?? undefined
137
+ },
138
+ message: `Retrieved **${result.data.length}** ${ctx.input.reportType} report bucket(s).${result.hasMore ? ' More data is available with nextPage.' : ''}`
139
+ };
140
+ })
141
+ .build();
@@ -0,0 +1,10 @@
1
+ export { sendMessage } from './send-message';
2
+ export { countTokens } from './count-tokens';
3
+ export { listModels } from './list-models';
4
+ export { manageMessageBatch } from './manage-message-batch';
5
+ export { manageOrganizationMembers } from './manage-organization-members';
6
+ export { manageWorkspaces } from './manage-workspaces';
7
+ export { manageApiKeys } from './manage-api-keys';
8
+ export { getOrganization } from './get-organization';
9
+ export { manageFiles } from './manage-files';
10
+ export { getUsageReport } from './get-usage-report';
@@ -0,0 +1,84 @@
1
+ import { SlateTool } from 'slates';
2
+ import { AnthropicClient } from '../lib/client';
3
+ import { spec } from '../spec';
4
+ import { z } from 'zod';
5
+
6
+ export let listModels = SlateTool.create(spec, {
7
+ name: 'List Models',
8
+ key: 'list_models',
9
+ description: `List available Claude models and their details. Retrieve information about context window size, capabilities, and model identifiers.
10
+ Optionally fetch details for a specific model by providing its ID.`,
11
+ tags: {
12
+ destructive: false,
13
+ readOnly: true
14
+ }
15
+ })
16
+ .input(
17
+ z.object({
18
+ modelId: z
19
+ .string()
20
+ .optional()
21
+ .describe(
22
+ 'Specific model ID to retrieve details for. If omitted, lists all available models.'
23
+ ),
24
+ limit: z.number().optional().describe('Maximum number of models to return when listing'),
25
+ afterId: z
26
+ .string()
27
+ .optional()
28
+ .describe('Pagination cursor: return models after this ID'),
29
+ beforeId: z
30
+ .string()
31
+ .optional()
32
+ .describe('Pagination cursor: return models before this ID')
33
+ })
34
+ )
35
+ .output(
36
+ z.object({
37
+ models: z
38
+ .array(z.record(z.string(), z.unknown()))
39
+ .optional()
40
+ .describe('List of available models (when listing)'),
41
+ model: z
42
+ .record(z.string(), z.unknown())
43
+ .optional()
44
+ .describe('Single model details (when fetching by ID)'),
45
+ hasMore: z
46
+ .boolean()
47
+ .optional()
48
+ .describe('Whether more models are available for pagination')
49
+ })
50
+ )
51
+ .handleInvocation(async ctx => {
52
+ let client = new AnthropicClient({
53
+ token: ctx.auth.token,
54
+ apiVersion: ctx.config.apiVersion
55
+ });
56
+
57
+ if (ctx.input.modelId) {
58
+ let model = await client.getModel(ctx.input.modelId);
59
+ return {
60
+ output: {
61
+ model,
62
+ models: undefined,
63
+ hasMore: undefined
64
+ },
65
+ message: `Retrieved details for model **${ctx.input.modelId}**.`
66
+ };
67
+ }
68
+
69
+ let result = await client.listModels({
70
+ limit: ctx.input.limit,
71
+ afterId: ctx.input.afterId,
72
+ beforeId: ctx.input.beforeId
73
+ });
74
+
75
+ return {
76
+ output: {
77
+ models: result.models,
78
+ model: undefined,
79
+ hasMore: result.hasMore
80
+ },
81
+ message: `Found **${result.models.length}** available model(s).${result.hasMore ? ' More models available with pagination.' : ''}`
82
+ };
83
+ })
84
+ .build();
@@ -0,0 +1,94 @@
1
+ import { SlateTool } from 'slates';
2
+ import { AnthropicClient } from '../lib/client';
3
+ import { anthropicServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ export let manageApiKeys = SlateTool.create(spec, {
8
+ name: 'Manage API Keys',
9
+ key: 'manage_api_keys',
10
+ description: `List, retrieve, and update organization API keys via the Admin API. View active, inactive, or archived keys filtered by workspace. Update keys to activate, deactivate, or rename them.
11
+ Requires an Admin API key (sk-ant-admin...).`,
12
+ constraints: [
13
+ 'Requires an Admin API key (sk-ant-admin...).',
14
+ 'Cannot create or delete API keys via this endpoint—only list and update.'
15
+ ],
16
+ tags: {
17
+ destructive: false,
18
+ readOnly: false
19
+ }
20
+ })
21
+ .input(
22
+ z.object({
23
+ action: z.enum(['list', 'get', 'update']).describe('Operation to perform'),
24
+ apiKeyId: z.string().optional().describe('API key ID (required for "get" and "update")'),
25
+ status: z
26
+ .enum(['active', 'inactive', 'archived'])
27
+ .optional()
28
+ .describe('Filter keys by status (for "list")'),
29
+ workspaceId: z.string().optional().describe('Filter keys by workspace ID (for "list")'),
30
+ name: z.string().optional().describe('New name for the API key (for "update")'),
31
+ keyStatus: z
32
+ .enum(['active', 'inactive'])
33
+ .optional()
34
+ .describe('New status for the API key (for "update")'),
35
+ limit: z.number().optional().describe('Max results for "list"'),
36
+ afterId: z.string().optional().describe('Pagination cursor for "list"')
37
+ })
38
+ )
39
+ .output(
40
+ z.object({
41
+ apiKeys: z
42
+ .array(z.record(z.string(), z.unknown()))
43
+ .optional()
44
+ .describe('List of API keys'),
45
+ apiKey: z.record(z.string(), z.unknown()).optional().describe('Updated API key details'),
46
+ hasMore: z.boolean().optional().describe('Whether more results are available')
47
+ })
48
+ )
49
+ .handleInvocation(async ctx => {
50
+ let client = new AnthropicClient({
51
+ token: ctx.auth.token,
52
+ apiVersion: ctx.config.apiVersion
53
+ });
54
+
55
+ switch (ctx.input.action) {
56
+ case 'list': {
57
+ let result = await client.listApiKeys({
58
+ limit: ctx.input.limit,
59
+ afterId: ctx.input.afterId,
60
+ status: ctx.input.status,
61
+ workspaceId: ctx.input.workspaceId
62
+ });
63
+ return {
64
+ output: { apiKeys: result.apiKeys, hasMore: result.hasMore },
65
+ message: `Found **${result.apiKeys.length}** API key(s).${result.hasMore ? ' More available with pagination.' : ''}`
66
+ };
67
+ }
68
+ case 'get': {
69
+ if (!ctx.input.apiKeyId) {
70
+ throw anthropicServiceError('apiKeyId is required for "get"');
71
+ }
72
+ let apiKey = await client.getApiKey(ctx.input.apiKeyId);
73
+ return {
74
+ output: { apiKey },
75
+ message: `Retrieved API key **${ctx.input.apiKeyId}**.`
76
+ };
77
+ }
78
+ case 'update': {
79
+ if (!ctx.input.apiKeyId) {
80
+ throw anthropicServiceError('apiKeyId is required for "update"');
81
+ }
82
+ let params: Record<string, unknown> = {};
83
+ if (ctx.input.name !== undefined) params.name = ctx.input.name;
84
+ if (ctx.input.keyStatus !== undefined) params.status = ctx.input.keyStatus;
85
+
86
+ let apiKey = await client.updateApiKey(ctx.input.apiKeyId, params);
87
+ return {
88
+ output: { apiKey },
89
+ message: `Updated API key **${ctx.input.apiKeyId}**.`
90
+ };
91
+ }
92
+ }
93
+ })
94
+ .build();
@@ -0,0 +1,149 @@
1
+ import { SlateTool } from 'slates';
2
+ import { AnthropicClient } from '../lib/client';
3
+ import { anthropicServiceError } from '../lib/errors';
4
+ import { spec } from '../spec';
5
+ import { z } from 'zod';
6
+
7
+ let fileOutputSchema = z.object({
8
+ fileId: z.string().describe('Anthropic file ID'),
9
+ type: z.string().optional().describe('Object type'),
10
+ filename: z.string().optional().describe('Original filename'),
11
+ mimeType: z.string().optional().describe('MIME type'),
12
+ sizeBytes: z.number().optional().describe('File size in bytes'),
13
+ createdAt: z.string().optional().describe('RFC 3339 creation timestamp'),
14
+ downloadable: z
15
+ .boolean()
16
+ .optional()
17
+ .describe('Whether this file can be downloaded through the Files API')
18
+ });
19
+
20
+ let fileContentOutputSchema = z.object({
21
+ fileId: z.string().describe('Anthropic file ID'),
22
+ contentBase64: z.string().describe('Downloaded file content encoded as base64'),
23
+ contentType: z.string().optional().describe('Downloaded response content type'),
24
+ sizeBytes: z.number().describe('Downloaded content size in bytes')
25
+ });
26
+
27
+ export let manageFiles = SlateTool.create(spec, {
28
+ name: 'Manage Files',
29
+ key: 'manage_files',
30
+ description: `Upload, list, retrieve metadata for, download, or delete Anthropic Files API files. Uploaded files can be referenced from Messages requests by file ID; generated downloadable files can be retrieved as base64 content.`,
31
+ instructions: [
32
+ 'For "create": provide filename and contentBase64; mimeType defaults to application/octet-stream.',
33
+ 'For "list": optionally provide limit, afterId, or beforeId.',
34
+ 'For "get", "download", and "delete": provide fileId.',
35
+ 'Uploaded files are not always downloadable; Anthropic only allows downloading files that are marked downloadable, such as files generated by code execution.'
36
+ ],
37
+ tags: {
38
+ destructive: true,
39
+ readOnly: false
40
+ }
41
+ })
42
+ .input(
43
+ z.object({
44
+ action: z
45
+ .enum(['create', 'list', 'get', 'download', 'delete'])
46
+ .describe('Operation to perform'),
47
+ fileId: z
48
+ .string()
49
+ .optional()
50
+ .describe('File ID (required for get, download, and delete)'),
51
+ filename: z.string().optional().describe('Filename for create'),
52
+ contentBase64: z.string().optional().describe('Base64 encoded file content for create'),
53
+ mimeType: z
54
+ .string()
55
+ .optional()
56
+ .describe('MIME type for create, e.g. text/plain or application/pdf'),
57
+ limit: z.number().optional().describe('Max files to return for list'),
58
+ afterId: z.string().optional().describe('Pagination cursor after this file ID'),
59
+ beforeId: z.string().optional().describe('Pagination cursor before this file ID')
60
+ })
61
+ )
62
+ .output(
63
+ z.object({
64
+ file: fileOutputSchema.optional().describe('File metadata'),
65
+ files: z.array(fileOutputSchema).optional().describe('List of files'),
66
+ fileContent: fileContentOutputSchema.optional().describe('Downloaded file content'),
67
+ deletedFile: z
68
+ .object({
69
+ fileId: z.string(),
70
+ type: z.string().optional()
71
+ })
72
+ .optional()
73
+ .describe('Deleted file result'),
74
+ hasMore: z.boolean().optional().describe('Whether more list results are available'),
75
+ firstId: z.string().optional().describe('First file ID in the page'),
76
+ lastId: z.string().optional().describe('Last file ID in the page'),
77
+ success: z.boolean().optional().describe('Whether a delete operation succeeded')
78
+ })
79
+ )
80
+ .handleInvocation(async ctx => {
81
+ let client = new AnthropicClient({
82
+ token: ctx.auth.token,
83
+ apiVersion: ctx.config.apiVersion
84
+ });
85
+
86
+ switch (ctx.input.action) {
87
+ case 'create': {
88
+ if (!ctx.input.filename || !ctx.input.contentBase64) {
89
+ throw anthropicServiceError('filename and contentBase64 are required for "create"');
90
+ }
91
+ let file = await client.createFile({
92
+ filename: ctx.input.filename,
93
+ contentBase64: ctx.input.contentBase64,
94
+ mimeType: ctx.input.mimeType
95
+ });
96
+ return {
97
+ output: { file },
98
+ message: `Uploaded file **${file.filename ?? ctx.input.filename}** as **${file.fileId}**.`
99
+ };
100
+ }
101
+ case 'list': {
102
+ let result = await client.listFiles({
103
+ limit: ctx.input.limit,
104
+ afterId: ctx.input.afterId,
105
+ beforeId: ctx.input.beforeId
106
+ });
107
+ return {
108
+ output: {
109
+ files: result.files,
110
+ hasMore: result.hasMore,
111
+ firstId: result.firstId,
112
+ lastId: result.lastId
113
+ },
114
+ message: `Found **${result.files.length}** file(s).${result.hasMore ? ' More files are available with pagination.' : ''}`
115
+ };
116
+ }
117
+ case 'get': {
118
+ if (!ctx.input.fileId) {
119
+ throw anthropicServiceError('fileId is required for "get"');
120
+ }
121
+ let file = await client.getFile(ctx.input.fileId);
122
+ return {
123
+ output: { file },
124
+ message: `Retrieved metadata for file **${file.fileId}**.`
125
+ };
126
+ }
127
+ case 'download': {
128
+ if (!ctx.input.fileId) {
129
+ throw anthropicServiceError('fileId is required for "download"');
130
+ }
131
+ let fileContent = await client.downloadFile(ctx.input.fileId);
132
+ return {
133
+ output: { fileContent },
134
+ message: `Downloaded file **${fileContent.fileId}** (${fileContent.sizeBytes} bytes).`
135
+ };
136
+ }
137
+ case 'delete': {
138
+ if (!ctx.input.fileId) {
139
+ throw anthropicServiceError('fileId is required for "delete"');
140
+ }
141
+ let deletedFile = await client.deleteFile(ctx.input.fileId);
142
+ return {
143
+ output: { deletedFile, success: true },
144
+ message: `Deleted file **${deletedFile.fileId}**.`
145
+ };
146
+ }
147
+ }
148
+ })
149
+ .build();