feat(plugin): flat tool draft with namespace
Effect draft.add accepts flat tool objects matching Promise ergonomics. Migrate the tool grouping concept fully to namespace: rename MCP's group helper/registration to namespace and thread namespace through the flat Effect and Promise draft declarations.
This commit is contained in:
parent
64ca9b8d77
commit
6714f74433
13 changed files with 157 additions and 88 deletions
|
|
@ -123,6 +123,17 @@ export function make(config: Config<any, any, any> | DynamicConfig): AnyTool {
|
|||
return makeTyped(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a flat tool declaration into its registration name, opaque Tool value,
|
||||
* and registration options. Narrowing on `jsonSchema` selects the matching
|
||||
* constructor, so no cast is needed to build the Tool from the flat union.
|
||||
*/
|
||||
export function fromFlat(flat: FlatDefinition<any, any, any> | FlatDynamicDefinition) {
|
||||
const { name, namespace, codemode, ...config } = flat
|
||||
const tool = "jsonSchema" in config ? makeDynamic(config) : makeTyped(config)
|
||||
return { name, tool, options: { namespace, codemode } satisfies RegisterOptions }
|
||||
}
|
||||
|
||||
function makeTyped<
|
||||
Input extends SchemaType<any>,
|
||||
Output extends SchemaType<any>,
|
||||
|
|
@ -212,14 +223,14 @@ export const validateName = (name: string) =>
|
|||
? Effect.void
|
||||
: Effect.fail(new RegistrationError({ name, message: `Invalid tool name: ${name}` }))
|
||||
|
||||
export const registrationEntries = (tools: Readonly<Record<string, AnyTool>>, group?: string) =>
|
||||
export const registrationEntries = (tools: Readonly<Record<string, AnyTool>>, namespace?: string) =>
|
||||
Object.entries(tools).map(([name, tool]) => {
|
||||
const normalized = name.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
const parent = group?.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
const parent = namespace?.replace(/[^a-zA-Z0-9_-]/g, "_")
|
||||
return {
|
||||
key: parent === undefined ? normalized : `${parent}_${normalized}`,
|
||||
name: normalized,
|
||||
group: parent,
|
||||
namespace: parent,
|
||||
tool,
|
||||
}
|
||||
})
|
||||
|
|
@ -271,15 +282,68 @@ export interface ToolExecuteAfterEvent {
|
|||
}
|
||||
|
||||
export interface RegisterOptions {
|
||||
readonly group?: string
|
||||
/** Dotted CodeMode path prefix, e.g. "slack.admin". */
|
||||
readonly namespace?: string
|
||||
/** Defaults to true. False exposes the tool directly to the provider. */
|
||||
readonly codemode?: boolean
|
||||
}
|
||||
|
||||
export type FlatDefinition<
|
||||
Input extends SchemaType<any>,
|
||||
Output extends SchemaType<any>,
|
||||
Structured extends SchemaType<any> = Output,
|
||||
> = {
|
||||
readonly name: string
|
||||
readonly namespace?: string
|
||||
readonly codemode?: boolean
|
||||
readonly description: string
|
||||
readonly input: Input
|
||||
readonly output: Output
|
||||
readonly structured?: Structured
|
||||
readonly toStructuredOutput?: Config<Input, Output, Structured>["toStructuredOutput"]
|
||||
readonly execute: Config<Input, Output, Structured>["execute"]
|
||||
readonly toModelOutput?: Config<Input, Output, Structured>["toModelOutput"]
|
||||
}
|
||||
|
||||
export type FlatDynamicDefinition = {
|
||||
readonly name: string
|
||||
readonly namespace?: string
|
||||
readonly codemode?: boolean
|
||||
readonly description: string
|
||||
readonly jsonSchema: JsonSchema.JsonSchema
|
||||
readonly outputSchema?: JsonSchema.JsonSchema
|
||||
readonly execute: DynamicConfig["execute"]
|
||||
}
|
||||
|
||||
export interface ToolDraft {
|
||||
add<
|
||||
Input extends SchemaType<any>,
|
||||
Output extends SchemaType<any>,
|
||||
Structured extends SchemaType<any> = Output,
|
||||
>(tool: FlatDefinition<Input, Output, Structured>): void
|
||||
add(tool: FlatDynamicDefinition): void
|
||||
add(name: string, tool: AnyTool, options?: RegisterOptions): void
|
||||
}
|
||||
|
||||
export type Registration = {
|
||||
readonly name: string
|
||||
readonly tool: AnyTool
|
||||
readonly options?: RegisterOptions
|
||||
}
|
||||
|
||||
/** Run a draft callback and collect the tools it declared, in registration order. */
|
||||
export function fromDraft(callback: (draft: ToolDraft) => void) {
|
||||
const registrations: Array<Registration> = []
|
||||
const add = (
|
||||
nameOrTool: string | FlatDefinition<any, any, any> | FlatDynamicDefinition,
|
||||
tool?: AnyTool,
|
||||
options?: RegisterOptions,
|
||||
) =>
|
||||
registrations.push(typeof nameOrTool === "string" ? { name: nameOrTool, tool: tool!, options } : fromFlat(nameOrTool))
|
||||
callback({ add })
|
||||
return registrations
|
||||
}
|
||||
|
||||
export interface ToolHooks {
|
||||
readonly "execute.before": ToolExecuteBeforeEvent
|
||||
readonly "execute.after": ToolExecuteAfterEvent
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ export type Definition<
|
|||
Structured extends SchemaType<any> = Output,
|
||||
> = {
|
||||
readonly name: string
|
||||
readonly options?: RegisterOptions
|
||||
readonly namespace?: string
|
||||
readonly codemode?: boolean
|
||||
readonly description: string
|
||||
readonly input: Input
|
||||
readonly output: Output
|
||||
|
|
@ -37,7 +38,8 @@ export type Definition<
|
|||
|
||||
export type DynamicDefinition = {
|
||||
readonly name: string
|
||||
readonly options?: RegisterOptions
|
||||
readonly namespace?: string
|
||||
readonly codemode?: boolean
|
||||
readonly description: string
|
||||
readonly jsonSchema: JsonSchema.JsonSchema
|
||||
readonly outputSchema?: JsonSchema.JsonSchema
|
||||
|
|
@ -67,12 +69,6 @@ export interface ToolExecuteAfterEvent {
|
|||
outputPaths?: ReadonlyArray<string>
|
||||
}
|
||||
|
||||
export interface RegisterOptions {
|
||||
readonly group?: string
|
||||
/** Defaults to true. False exposes the tool directly to the provider. */
|
||||
readonly codemode?: boolean
|
||||
}
|
||||
|
||||
export interface ToolDraft {
|
||||
add<
|
||||
Input extends SchemaType<any>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue