MCP::Tools
Inherits Utils::Paths
Constants
Log = ::Log.for("mcp")
NAME_REGEX = /^([a-zA-Z0-9_-]+|hashtag#[a-zA-Z0-9_-]+|mention@[a-zA-Z0-9_@.-]+)$/
TOOL_DEFINITIONS = [{name: "paginate_collection",
description: "Paginate through collections of ActivityPub objects, activities, and actors. Use this tool when you want to inspect the contents of a collection.\n\nPagination is cursor-based. The response includes `next_older_than` and/or `next_newer_than` cursor values. To fetch the next page in a direction, pass the corresponding cursor as `older_than` or `newer_than` on the next call. The absence of a cursor in the response means there are no more items in that direction.\n\nItems are returned newest-first.",
properties: [{name: "name", type: "string", description: "Name of the collection to paginate", required: true, matches: NAME_REGEX}, {name: "older_than", type: "integer", description: "Return items older than this id (optional). Pass the `next_older_than` value from a previous response.", minimum: 1, default: nil}, {name: "newer_than", type: "integer", description: "Return items newer than this id (optional). Pass the `next_newer_than` value from a previous response.", minimum: 1, default: nil}, {name: "limit", type: "integer", description: "Number of items per page (optional, defaults to 10, maximum 20)", minimum: 1, maximum: 20, default: 10}]}, {name: "count_collection_since",
description: "Count items in ActivityPub collections since a given time. Use this tool when you want to know if new items have been added in the last day/week/month.", properties: [{name: "name", type: "string", description: "Name of the collection to count", required: true, matches: NAME_REGEX}, {name: "since", type: "time", description: "Time (RFC3339) to count from", required: true}]}, {name: "get_thread",
description: "Retrieve thread structure, metadata, and summary data. Large threads may have hundreds of objects, so this tool supports pagination. This tool retrieves the entire thread for the given `object_id` starting at the root, not only the subthread.\n\n**Two Modes of Operation:**\n\n1. **Initial Query Mode**: Provide `object_id` (plus optional `projection` and `page_size`) to start traversing a thread.\n Returns summary data and the first page of objects. If there are more pages, includes a `cursor`.\n\n2. **Pagination Mode**: Provide only `cursor` (from a previous response) to fetch the next page.\n Returns the next page of objects. If there are more pages, includes a `cursor`.\n Do not include `object_id`, `projection`, or `page_size`.\n\n**Usage Examples:**\n- Start: `{\"object_id\": 123, \"projection\": \"metadata\", \"page_size\": 20}`\n- Continue: `{\"cursor\": \"eyJwYWdlcl9pZ...\"}`\n\n**Important:** You must provide EITHER `object_id` OR `cursor`, but not both.",
properties: [{name: "object_id", type: "integer", description: "Database ID of any object in the thread. Required for initial query, omit when using cursor.", required: false, minimum: 1}, {name: "projection", type: "string", description: "Data fields to include: 'minimal' (IDs and structure only) or 'metadata' (adds authors, timestamps). Only used with object_id.", required: false, enum: ["minimal", "metadata"], default: "metadata"}, {name: "page_size", type: "integer", description: "Number of objects per page. Only used with object_id.", required: false, minimum: 1, maximum: 100, default: 25}, {name: "cursor", type: "string", description: "Opaque pagination cursor from previous get_thread response. Use ONLY this parameter to fetch next page.", required: false}]}, {name: "analyze_thread",
description: "Analyze thread structure and identify key participants and notable branches. Use this before reading thread content to understand the conversation landscape and identify which posts are most relevant to examine. This is especially useful with large threads.\n\n**Returns:**\n- Basic statistics (total posts, unique authors, max depth, analysis duration)\n- Key participants (original poster + top 5 most active posters with their posts)\n- Notable branches (conversation subtrees with ≥5 posts)\n- Timeline histogram (temporal distribution of posts)",
properties: [{name: "object_id", type: "integer", description: "Database ID of any object in the thread", required: true, minimum: 1}]}, {name: "read_resources",
description: "Read one or more resources by URI (format \"ktistec://{resource}/{id*}\"). Supports all resource types including templated resources (actors, objects) and static resources (information, users). Supports batched reads (comma-separated IDs of resources of the same type). Use this tool as a universal fallback when resources are not supported by an MCP client.",
properties: [{name: "uris", type: "array", description: "Resource URIs to read (e.g., ['ktistec://actors/123,456', 'ktistec://objects/456,789'])", required: true, items: "string"}]}] of ToolDefinition
Class methods
handle_tools_list(request : JSON::RPC::Request) : JSON::Any
Sourceresult_pager
Sourceresult_pager=(result_pager)
SourceMacros
def_tool(name, description, properties = [] of ToolPropertyDefinition, &block)
Defines an MCP tool.
Arguments:
- name: Tool name
- description: Tool description
- properties: Array of ToolPropertyDefinition (type, description, required, etc.)
- block: Tool implementation
The block can access validated parameters via arguments["param_name"]?
and must return JSON::Any.