OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-cogserver / opencog / cogserver / mcp-tools / McpPlugAtomSpace.cc

162 lines · cc ref:
/*
 * McpPlugAtomSpace.cc
 *
 * MCP Plugin for AtomSpace operations
 * Copyright (c) 2025 Linas Vepstas
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "McpPlugAtomSpace.h"
#include <opencog/persist/json/JSCommands.h>

using namespace opencog;

// Helper to add a tool description
static void add_tool(std::string& json, const std::string& name, 
                     const std::string& description,
                     const std::string& schema,
                     bool last = false)
{
	json += "\t{\n";
	json += "\t\t\"name\": \"" + name + "\",\n";
	json += "\t\t\"description\": \"" + description + "\",\n";
	json += "\t\t\"inputSchema\": " + schema + "\n";
	json += "\t}";
	if (!last) json += ",";
	json += "\n";
}

// Tool descriptions for all AtomSpace operations
std::string McpPlugAtomSpace::get_tool_descriptions() const
{
	std::string json = "[\n";

	// version
	add_tool(json, "version", "Get the AtomSpace version string",
		"{\"type\": \"object\", \"properties\": {}, \"required\": []}");

	// getSubTypes
	add_tool(json, "getSubTypes", "Get all subtypes of a given atom type. Useful for exploring the type hierarchy.",
		"{\"type\": \"object\", \"properties\": {"
		"\"type\": {\"type\": \"string\", \"description\": \"The atom type to get subtypes of. Examples: 'Node', 'Link', 'Value'. Returns all types that inherit from this type.\"}, "
		"\"recursive\": {\"type\": \"boolean\", \"description\": \"If true, gets all descendants recursively. If false (default), gets only immediate children.\"}}, "
		"\"required\": [\"type\"]}");

	// getSuperTypes
	add_tool(json, "getSuperTypes", "Get all supertypes of a given atom type. Useful for exploring the type hierarchy.",
		"{\"type\": \"object\", \"properties\": {"
		"\"type\": {\"type\": \"string\", \"description\": \"The atom type to get supertypes of. Examples: 'Concept', 'Edge', 'FloatValue'. Returns all types this type inherits from.\"}, "
		"\"recursive\": {\"type\": \"boolean\", \"description\": \"If true, gets all ancestors recursively up to TopType. If false (default), gets only immediate parents.\"}}, "
		"\"required\": [\"type\"]}");

	// reportCounts
	add_tool(json, "reportCounts", "A report of how many Atoms there are in the AtomSpace, organized by Atom type.",
		"{\"type\": \"object\", \"properties\": {}, \"required\": []}");

	// getAtoms
	add_tool(json, "getAtoms", "Get all atoms of a specific type from the AtomSpace. WARNING: May return large results - check count with reportCounts first.",
		"{\"type\": \"object\", \"properties\": {"
		"\"type\": {\"type\": \"string\", \"description\": \"The atom type to retrieve. Examples: 'Concept', 'Predicate', 'Edge', 'List'. Use getSubTypes/getSuperTypes to explore type hierarchy.\"}, "
		"\"subclass\": {\"type\": \"boolean\", \"description\": \"Whether to include atoms of subtypes. If true, retrieves all subtypes of the given type.\"}}, "
		"\"required\": [\"type\"]}");

	// haveNode
	add_tool(json, "haveNode", "Check if a node exists in the AtomSpace",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the node, e.g. (Concept \\\"cat\\\")\"}}, "
		"\"required\": [\"atomese\"]}");

	// haveLink
	add_tool(json, "haveLink", "Check if a link exists in the AtomSpace",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the link, e.g. (List (Concept \\\"a\\\") (Concept \\\"b\\\"))\"}}, "
		"\"required\": [\"atomese\"]}");

	// haveAtom
	add_tool(json, "haveAtom", "Check if an atom exists in the AtomSpace",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom\"}}, "
		"\"required\": [\"atomese\"]}");

	// makeAtom
	add_tool(json, "makeAtom", "Create an atom in the AtomSpace",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom to create. Examples: (Concept \\\"cat\\\"), (List (Concept \\\"a\\\") (Concept \\\"b\\\")), (Edge (Predicate \\\"likes\\\") (List (Concept \\\"Alice\\\") (Concept \\\"Bob\\\"))). Nodes and Links can be arbitrarily nested.\"}}, "
		"\"required\": [\"atomese\"]}");

	// getIncoming
	add_tool(json, "getIncoming", "Get all links that contain a given atom in their outgoing set",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom\"}}, "
		"\"required\": [\"atomese\"]}");

	// getKeys
	add_tool(json, "getKeys", "Get all keys attached to an atom",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom\"}}, "
		"\"required\": [\"atomese\"]}");

	// getValueAtKey
	add_tool(json, "getValueAtKey", "Get the value on an atom located at a given key. Returns a Value (FloatValue, StringValue, LinkValue, or Atom) in s-expression format.",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom. Example: (Anchor \\\"my-data\\\")\"}, "
		"\"key\": {\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the key atom. Example: (Predicate \\\"my-key\\\")\"}}, \"required\": [\"atomese\"]}}, "
		"\"required\": [\"atomese\", \"key\"]}");

	// getValues
	add_tool(json, "getValues", "Get all values attached to an atom. Returns an association list (alist) of (key . value) pairs in s-expression format.",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom. Example: (Anchor \\\"my-data\\\")\"}}, "
		"\"required\": [\"atomese\"]}");

	// setValue
	add_tool(json, "setValue", "Set a value on an atom with a given key",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom. Example: (Anchor \\\"my-data\\\")\"}, "
		"\"key\": {\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the key atom. Example: (Predicate \\\"my-key\\\")\"}}, \"required\": [\"atomese\"]}, "
		"\"value\": {\"type\": \"object\", \"description\": \"The value to set. PREFERRED: Use 'atomese' property with s-expression. Examples: {\\\"atomese\\\": \\\"(FloatValue 1.5 2.7 3.14)\\\"}, {\\\"atomese\\\": \\\"(StringValue \\\\\\\"hello\\\\\\\" \\\\\\\"world\\\\\\\")\\\"}, {\\\"atomese\\\": \\\"(LinkValue (Concept \\\\\\\"A\\\\\\\") (Concept \\\\\\\"B\\\\\\\"))\\\"}. Alternative: verbose JSON format {\\\"type\\\": \\\"FloatValue\\\", \\\"value\\\": [1.5, 2.7, 3.14]}.\", \"properties\": {\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the value (PREFERRED). Examples: (FloatValue 1.0 2.0), (StringValue \\\\\\\"text\\\\\\\"), (LinkValue (Concept \\\\\\\"X\\\\\\\") (Concept \\\\\\\"Y\\\\\\\"))\"}, \"type\": {\"type\": \"string\", \"description\": \"Type name for verbose JSON (not recommended)\"}, \"value\": {\"description\": \"Value data for verbose JSON (not recommended)\"}}}}, "
		"\"required\": [\"atomese\", \"key\", \"value\"]}");

	// execute
	add_tool(json, "execute", "Execute an executable atom and get the result. WARNING: Execution has side effects and may modify AtomSpace contents or external systems. Returns a Value.",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the executable atom. Examples: (PlusLink (Number 1) (Number 2)), (ValueOf (Concept \\\"foo\\\") (Predicate \\\"key\\\")), (Query ...). Many Link types are executable - see wiki for details.\"}}, "
		"\"required\": [\"atomese\"]}");

	// extract
	add_tool(json, "extract", "Remove an atom from the AtomSpace. WARNING: Irreversible operation.",
		"{\"type\": \"object\", \"properties\": {"
		"\"atomese\": {\"type\": \"string\", \"description\": \"S-expression for the atom to remove. Example: (Concept \\\"obsolete\\\")\"}, "
		"\"recursive\": {\"type\": \"boolean\", \"description\": \"If true, recursively removes all Links containing this atom. If false (default), only removes the atom if nothing references it.\"}}, "
		"\"required\": [\"atomese\"]}", true);  // last = true

	json += "]";
	return json;
}

std::string McpPlugAtomSpace::invoke_tool(const std::string& tool_name,
                                          const std::string& arguments) const
{
	// Construct the MCP command format that JSCommands expects
	std::string mcp_command = "{ \"tool\": \"" + tool_name + "\", \"params\": " + arguments + "}";

	// Use JSCommands to process the command
	return JSCommands::interpret_command(_as, mcp_command);
}