Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 77 additions & 3 deletions packages/core/src/types/spec.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ export interface ClientCapabilities {
};
};
};
/**
* Present if the client supports declarative file inputs for tools and
* elicitation. When declared, servers MAY include `inputFiles` on {@link Tool}
* definitions and `requestedFiles` on {@link ElicitRequestFormParams}.
*/
fileInputs?: object;
}

/**
Expand Down Expand Up @@ -1293,12 +1299,49 @@ export interface Tool extends BaseMetadata, Icons {
*/
annotations?: ToolAnnotations;

/**
* Declares which arguments in `inputSchema` are file inputs. Keys MUST match
* property names in `inputSchema.properties`, and the corresponding schema
* properties MUST be `{"type": "string", "format": "uri"}` or an array thereof.
*
* Servers MUST NOT include this field unless the client declared the
* `fileInputs` capability during initialization.
*
* Clients SHOULD render a native file picker for these arguments. Selected files
* are encoded as RFC 2397 data URIs: `data:<mediatype>;name=<filename>;base64,<data>`,
* where the `name=` parameter (percent-encoded) carries the original filename.
*/
inputFiles?: { [argName: string]: FileInputDescriptor };

/**
* See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage.
*/
_meta?: { [key: string]: unknown };
}

/**
* Describes a single file input argument for a tool or elicitation form.
* Provides optional hints for client-side file picker filtering and validation.
* All fields are advisory; servers MUST still validate inputs independently.
*
* @category `tools/list`
*/
export interface FileInputDescriptor {
/**
* MIME type patterns that the server will accept for this input.
* Supports exact types (e.g., `"image/png"`) and wildcard subtypes
* (e.g., `"image/*"`). If omitted, any file type is accepted.
*/
accept?: string[];

/**
* Maximum file size in bytes (decoded size, per file). Servers SHOULD reject
* larger files with JSON-RPC `-32602` (Invalid Params) and the structured reason
* `"file_too_large"`.
*/
maxSize?: number;
}

/* Tasks */

/**
Expand Down Expand Up @@ -2159,6 +2202,21 @@ export interface ElicitRequestFormParams extends TaskAugmentedRequestParams {
};
required?: string[];
};

/**
* Declares which fields in `requestedSchema` are file inputs. Keys MUST match
* property names in `requestedSchema.properties`, and the corresponding schema
* properties MUST be a {@link StringSchema} with `format: "uri"` or a
* {@link StringArraySchema} whose `items` has `format: "uri"`.
*
* Servers MUST NOT include this field unless the client declared the
* `fileInputs` capability during initialization.
*
* Clients SHOULD render a native file picker for these fields. Selected files
* are encoded as RFC 2397 data URIs: `data:<mediatype>;name=<filename>;base64,<data>`,
* where the `name=` parameter (percent-encoded) carries the original filename.
*/
requestedFiles?: { [fieldName: string]: FileInputDescriptor };
}

/**
Expand Down Expand Up @@ -2209,12 +2267,12 @@ export interface ElicitRequest extends JSONRPCRequest {
}

/**
* Restricted schema definitions that only allow primitive types
* without nested objects or arrays.
* Restricted schema definitions that only allow primitive types and
* flat arrays of strings (for multi-file inputs), without nested objects.
*
* @category `elicitation/create`
*/
export type PrimitiveSchemaDefinition = StringSchema | NumberSchema | BooleanSchema | EnumSchema;
export type PrimitiveSchemaDefinition = StringSchema | NumberSchema | BooleanSchema | EnumSchema | StringArraySchema;

/**
* @category `elicitation/create`
Expand Down Expand Up @@ -2433,6 +2491,22 @@ export interface LegacyTitledEnumSchema {
// Union type for all enum schemas
export type EnumSchema = SingleSelectEnumSchema | MultiSelectEnumSchema | LegacyTitledEnumSchema;

/**
* Schema for a flat array of strings. Intended primarily for multi-file
* inputs in elicitation forms, where each item is a data URI string with
* `format: "uri"`. Items MUST use {@link StringSchema}; nesting is not permitted.
*
* @category `elicitation/create`
*/
export interface StringArraySchema {
type: 'array';
items: StringSchema;
title?: string;
description?: string;
minItems?: number;
maxItems?: number;
}

/**
* The client's response to an elicitation request.
*
Expand Down
81 changes: 78 additions & 3 deletions packages/core/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,13 @@ export const ClientCapabilitiesSchema = z.object({
/**
* Present if the client supports task creation.
*/
tasks: ClientTasksCapabilitySchema.optional()
tasks: ClientTasksCapabilitySchema.optional(),
/**
* Present if the client supports declarative file inputs for tools and
* elicitation. When declared, servers MAY include `inputFiles` on {@linkcode Tool}
* definitions and `requestedFiles` on form-mode elicitation parameters.
*/
fileInputs: AssertObjectSchema.optional()
});

export const InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
Expand Down Expand Up @@ -1374,6 +1380,26 @@ export const ToolExecutionSchema = z.object({
taskSupport: z.enum(['required', 'optional', 'forbidden']).optional()
});

/**
* Describes a single file input argument for a tool or elicitation form.
* Provides optional hints for client-side file picker filtering and validation.
* All fields are advisory; servers MUST still validate inputs independently.
*/
export const FileInputDescriptorSchema = z.object({
/**
* MIME type patterns that the server will accept for this input.
* Supports exact types (e.g., `"image/png"`) and wildcard subtypes
* (e.g., `"image/*"`). If omitted, any file type is accepted.
*/
accept: z.array(z.string()).optional(),
/**
* Maximum file size in bytes (decoded size, per file). Servers SHOULD reject
* larger files with JSON-RPC `-32602` (Invalid Params) and the structured
* reason `"file_too_large"`.
*/
maxSize: z.number().int().optional()
});

/**
* Definition for a tool the client can call.
*/
Expand Down Expand Up @@ -1416,6 +1442,19 @@ export const ToolSchema = z.object({
* Execution-related properties for this tool.
*/
execution: ToolExecutionSchema.optional(),
/**
* Declares which arguments in `inputSchema` are file inputs. Keys MUST match
* property names in `inputSchema.properties`, and the corresponding schema
* properties MUST be `{"type": "string", "format": "uri"}` or an array thereof.
*
* Servers MUST NOT include this field unless the client declared the
* `fileInputs` capability during initialization.
*
* Clients SHOULD render a native file picker for these arguments. Selected files
* are encoded as RFC 2397 data URIs: `data:<mediatype>;name=<filename>;base64,<data>`,
* where the `name=` parameter (percent-encoded) carries the original filename.
*/
inputFiles: z.record(z.string(), FileInputDescriptorSchema).optional(),

/**
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
Expand Down Expand Up @@ -1970,10 +2009,30 @@ export const MultiSelectEnumSchemaSchema = z.union([UntitledMultiSelectEnumSchem
*/
export const EnumSchemaSchema = z.union([LegacyTitledEnumSchemaSchema, SingleSelectEnumSchemaSchema, MultiSelectEnumSchemaSchema]);

/**
* Schema for a flat array of strings. Intended primarily for multi-file
* inputs in elicitation forms, where each item is a data URI string with
* `format: "uri"`. Items MUST use {@linkcode StringSchema}; nesting is not permitted.
*/
export const StringArraySchemaSchema = z.object({
type: z.literal('array'),
items: StringSchemaSchema,
title: z.string().optional(),
description: z.string().optional(),
minItems: z.number().int().optional(),
maxItems: z.number().int().optional()
});

/**
* Union of all primitive schema definitions.
*/
export const PrimitiveSchemaDefinitionSchema = z.union([EnumSchemaSchema, BooleanSchemaSchema, StringSchemaSchema, NumberSchemaSchema]);
export const PrimitiveSchemaDefinitionSchema = z.union([
EnumSchemaSchema,
BooleanSchemaSchema,
StringSchemaSchema,
NumberSchemaSchema,
StringArraySchemaSchema
]);

/**
* Parameters for an `elicitation/create` request for form-based elicitation.
Expand All @@ -1997,7 +2056,21 @@ export const ElicitRequestFormParamsSchema = TaskAugmentedRequestParamsSchema.ex
type: z.literal('object'),
properties: z.record(z.string(), PrimitiveSchemaDefinitionSchema),
required: z.array(z.string()).optional()
})
}),
/**
* Declares which fields in `requestedSchema` are file inputs. Keys MUST match
* property names in `requestedSchema.properties`, and the corresponding schema
* properties MUST be a {@linkcode StringSchema} with `format: "uri"` or a
* {@linkcode StringArraySchema} whose `items` has `format: "uri"`.
*
* Servers MUST NOT include this field unless the client declared the
* `fileInputs` capability during initialization.
*
* Clients SHOULD render a native file picker for these fields. Selected files
* are encoded as RFC 2397 data URIs: `data:<mediatype>;name=<filename>;base64,<data>`,
* where the `name=` parameter (percent-encoded) carries the original filename.
*/
requestedFiles: z.record(z.string(), FileInputDescriptorSchema).optional()
});

/**
Expand Down Expand Up @@ -2516,6 +2589,7 @@ export type PromptListChangedNotification = Infer<typeof PromptListChangedNotifi
/* Tools */
export type ToolAnnotations = Infer<typeof ToolAnnotationsSchema>;
export type ToolExecution = Infer<typeof ToolExecutionSchema>;
export type FileInputDescriptor = Infer<typeof FileInputDescriptorSchema>;
export type Tool = Infer<typeof ToolSchema>;
export type ListToolsRequest = Infer<typeof ListToolsRequestSchema>;
export type ListToolsResult = Infer<typeof ListToolsResultSchema>;
Expand Down Expand Up @@ -2570,6 +2644,7 @@ export type UntitledMultiSelectEnumSchema = Infer<typeof UntitledMultiSelectEnum
export type TitledMultiSelectEnumSchema = Infer<typeof TitledMultiSelectEnumSchemaSchema>;
export type SingleSelectEnumSchema = Infer<typeof SingleSelectEnumSchemaSchema>;
export type MultiSelectEnumSchema = Infer<typeof MultiSelectEnumSchemaSchema>;
export type StringArraySchema = Infer<typeof StringArraySchemaSchema>;

export type PrimitiveSchemaDefinition = Infer<typeof PrimitiveSchemaDefinitionSchema>;
export type ElicitRequestParams = Infer<typeof ElicitRequestParamsSchema>;
Expand Down
10 changes: 9 additions & 1 deletion packages/core/test/spec.types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,10 @@ const sdkTypeChecks = {
sdk = spec;
spec = sdk;
},
StringArraySchema: (sdk: SDKTypes.StringArraySchema, spec: SpecTypes.StringArraySchema) => {
sdk = spec;
spec = sdk;
},
JSONRPCErrorResponse: (sdk: SDKTypes.JSONRPCErrorResponse, spec: SpecTypes.JSONRPCErrorResponse) => {
sdk = spec;
spec = sdk;
Expand Down Expand Up @@ -629,6 +633,10 @@ const sdkTypeChecks = {
sdk = spec;
spec = sdk;
},
FileInputDescriptor: (sdk: SDKTypes.FileInputDescriptor, spec: SpecTypes.FileInputDescriptor) => {
sdk = spec;
spec = sdk;
},
TaskStatus: (sdk: SDKTypes.TaskStatus, spec: SpecTypes.TaskStatus) => {
sdk = spec;
spec = sdk;
Expand Down Expand Up @@ -714,7 +722,7 @@ describe('Spec Types', () => {
it('should define some expected types', () => {
expect(specTypes).toContain('JSONRPCNotification');
expect(specTypes).toContain('ElicitResult');
expect(specTypes).toHaveLength(145);
expect(specTypes).toHaveLength(147);
});

it('should have up to date list of missing sdk types', () => {
Expand Down
Loading
Loading