We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jliocsar/bitbucket-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
tool-declaration.civet•5.21 kB
{ z, type ZodType, type ZodRawShape, type Primitive } from zod
type { ToolAnnotations, ServerRequest, ServerNotification } from @modelcontextprotocol/sdk/types.js
type { McpServer } from @modelcontextprotocol/sdk/server/mcp.js
type { RequestHandlerExtra } from @modelcontextprotocol/sdk/shared/protocol.js
{ IS_DEV } from ./const.civet
// Translates the input/output schema to a basic object type
// Apparently Zod didn't have this feature (maybe it's skill issue...?)
// Maybe I should just use the `zod-to-json-schema` package? Not sure...
export TInferedSchema<T> ::= [K in keyof T]: if T[K] < ZodType then z.infer<T[K]> else never
export TSerializable<T> ::= [K in keyof T]: Primitive | TSerializable<T[K]> | TSerializable<T[K]>[]
export ToolDeclarationConfig ::= {
inputSchema: if ZodRawShape < infer TInputSchema then TInputSchema else never
outputSchema?: if ZodRawShape < infer TOutputSchema then TOutputSchema else never
annotations?: ToolAnnotations
title: string
description: string
}
export ToolDeclarationRunCallback<TConfig < ToolDeclarationConfig = ToolDeclarationConfig, TConstructor < new (...args: any[]) => any = new () => any> ::=
(
@: ToolDeclaration<TConfig, TConstructor> & InstanceType<TConstructor>,
args: TInferedSchema<if TConfig["inputSchema"] < infer TInputSchema then TInputSchema else never>,
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
) -> Promise<
TSerializable<TInferedSchema<TConfig["outputSchema"]>> | TSerializable<TInferedSchema<TConfig["outputSchema"]>>[] | null
>
export ToolDeclarationArgs<TConfig < ToolDeclarationConfig = ToolDeclarationConfig, TConstructor < new (...args: any[]) => any = new () => any> ::= {
config: TConfig
run: ToolDeclarationRunCallback<TConfig, TConstructor>
name: string
}
// Wraps the tool declaration in a class to make it easier to use
// For now some of the stuff is injected directly into this class (like `bitbucket`), but that
// could change to be injected into the `run` function instead or something of the sort
export class ToolDeclaration<TConfig < ToolDeclarationConfig = ToolDeclarationConfig, TConstructor < new (...args: any[]) => any = new () => any> {
public readonly config: TConfig
public readonly run: ToolDeclarationRunCallback<TConfig, TConstructor>
public readonly name: string
@(args: ToolDeclarationArgs<TConfig, TConstructor>) {
@config = args.config
@run = args.run.bind @ as InstanceType<TConstructor>
@name = args.name
}
}
sendNotificationFromExtra := (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) ->
<T>(level: "error" | "warning" | "info" | "debug", data: T) ->
extra.sendNotification {
method: "notifications/message",
params: {
level,
data
}
}
Ok<T> ::= {
ok: true
data: T
}
Fail<T> ::= {
ok: false
error: T
}
serializeResult := <T>(result: Ok<T> | Fail<T>) ->
if result.ok {
JSON.stringify { ok: true, data: result.data }
} else {
JSON.stringify { ok: false, error: result.error }
}
safeActionCallToOutput := <T < ZodRawShape, TMember < ToolDeclaration>(member: TMember) ->
(args: TInferedSchema<T>, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) -> {
sendNotification := sendNotificationFromExtra extra
content: []
type: "text",
text: (
try
logged := {
member.name,
config: {
member.config.title
member.config.description
}
}
if IS_DEV {
sendNotification "debug", logged
}
run := member@run
result := await run args, extra
// We want only JSON-serializable results
if result is null {
return serializeResult { ok: false, error: "Empty result" }
}
// If it's an object (and not null), we want to return it as a JSON string
if typeof result is 'object' {
return serializeResult { ok: true, data: result }
}
catch actionError
sendNotification "error", {
error: (actionError as Error).message
}
serializeResult { ok: true, data: null }
)
}
/**
* Registers all tools with the server
* @param tools - The tools to register
* @param server - The server to register the tools with
* @returns The server with the tools registered
*/
export registerTools := <TConfig < ToolDeclarationConfig, T < ToolDeclaration<TConfig>>(tools: Record<string, T>) -> (server: McpServer) -> {
// Register tools based on the glob pattern
for await member of Object.values tools {
// For each exported member, if it's a tool declaration, register it with the server
// Filter out exports that aren't tool declarations
unless member instanceof ToolDeclaration {
continue
}
// Register the tool with the server
server.registerTool(
member.name,
member.config,
// Wrap the run call in a try/catch and return the result in the MCP format
// @ts-expect-error FIXME
safeActionCallToOutput member
)
}
// Return the server to allow for chaining
server
}