Skip to content
Agentmart
Submit a tool

lcp_tool_development

Complete guide for authoring LCP tool sets: binding discovery, AST schema generation, execution contract, host configuration injection, human-in-the-loop…

Skill by ParisNeo

IndexedApache-2.0★ 13
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/parisneo-lcp-tool-development

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Complete guide for authoring LCP tool sets: binding discovery, AST schema generation, execution contract, host configuration injection, human-in-the-loop validation, dynamic registration, and mounting APIs for CLI and GUI environments.

Use cases: documents

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/lcp-tool-development

What's inside

Skill name
lcp_tool_development
Description
Complete guide for authoring LCP tool sets: binding discovery, AST schema generation, execution contract, host configuration injection, human-in-the-loop validation, dynamic registration, and mounting APIs for CLI and GUI environments.
License
Apache-2.0
Instructions
608 lines in SKILL.md · view on GitHub

Agent Skills spec issues

  • name: missing
  • body: 608 lines (spec recommends under 500)

Files (1)

  • SKILL.md

SKILL.md (first lines)

# LCP Tool Architecture & Human-in-the-Loop Engineering

The **LollmsCommunicationProtocol (LCP)** is the lightweight, zero-dependency in-process tool execution engine of `lollms_client`, implemented by the `LCPBinding` class (`src/lollms_client/tools_bindings/lcp/__init__.py`, module attribute `BindingName = "LCPBinding"`). It discovers, parses, and executes Python tools in a sandboxed workspace environment.

---

## 1. Core LCP Tool Design Doctrine

1. **Agnostic & Decoupled**: LCP tools are plain Python functions. They operate strictly on primitive arguments (`str`, `int`, `float`, `bool`, `list`, `dict`) and return standard dictionaries. Tools do NOT know about discussions, artefacts, or clients.
2. **Current Working Directory (CWD) Sandbox**: The orchestrator changes the process CWD to the isolated workspace folder before invoking a tool. Tools resolve workspace files using relative paths (`Path("data.csv")`).
3. **AST Schema Generation — no `description.yaml`**: The binding does NOT read any per-tool description file. Schemas are generated purely from Python source: function names (`tool_*`), type annotations, defaults, and docstrings, parsed with the `ast` module at discovery time.
4. **Tool names = function names**: The LLM calls `tool_execute_query`, not the file stem. There is no `execute` entry-point fallback. Every function whose name starts with `tool_` is registered as a tool (nested functions included); helpers without the prefix are invisible to the LLM.

---

## 2. Tool Discovery Contract (How the Binding Finds Your Tools)

`LCPBinding` locates tools from its constructor configuration:

| Parameter | Behavior |
| :--- | :--- |
| `tools_folders` (or `tools_folder_path`) | A path or list of paths to scan. Accepts `str`, `Path`, or `list`. |
| `tool_files` | An explicit path or list of `.py` files to load. **Providing `tool_files` implies an empty folder scan** — folder discovery is skipped. |
| *(neither given)* | Defaults to the built-in `tools_bindings/lcp/default_tools` folder. |

### Folder scanning rules (`_discover_local_tools`)

For each configured folder, for each item on disk:

- **Sub-directory `X/`**: the primary module is `X/X.py` (directory name must match the file name). If it exists, it is loaded, then **all sibling `*.py` files** in `X/` are loaded as well. If `X/X.py` does not exist, any top-level `.py` files inside `X/` are used as fallback modules.
- **Loose `.py` file** directly in the folder: loaded directly.
- **`__init__.py` is always skipped.**
- **Dedup by tool name**: if the same `tool_*` function name appears in multiple files, the first discovery wins; later duplicates are silently ignored.

> **Important**: discovery is *lazy registration*. At construction time the binding only parses files with AST and records tool specs; module code is NOT executed until first use (see §7). This keeps startup fast and side-effect-free.

---

## 3. Anatomy of a Multi-Tool LCP Library

Create a `.py` file inside your tools directory (e.g. `my_tools/database_ops.py`):

```python
"""
database_ops.py — Multi-tool library for SQLite querying and maintenance.
"""
import sqlite3
from typing import Any, Dict, Optional
from pathlib import Path

TOOL_LIBRARY_NAME = "Database Operations"
TOOL_LIBRARY_DESC = "Executes queries and schema inspections on SQLite databases in the workspace."
TOOL_LIBRARY_ICON = "🗄️"

# Host configuration state
AUTONOMY_LEVEL = "safe"
_CONFIRM_HANDLER = None

def init_tools_library(config: Optional[dict] = None) -> None:
    """Invoked lazily when the tool module is first loaded or configured by the host."""
    global AUTONOMY_LEVEL, _CONFIRM_HANDLER
    if config and isinstance(config, dict):
        AUTONOMY_LEVEL = config.get("autonomy_level", "safe")
        _CONFIRM_HANDLER = config.get("confirm_handler")

def tool_database_ops_prompt() -> str:
    """Dynamic prompt provider — NOT registered as a tool."""
    db_files = [f.name for f in Path(".").glob("*.db")]
    return f"Active workspace SQLite databases available: {db_files or 'None'}"

def tool_execute_query(database_name: str, query: str) -> Dict[str, Any]:
    """
    Executes a SELECT query on a workspace SQLite database.

    Args:
        database_name (str): Name of the .db file in the workspace.
        query (str): The SQL SELECT query to execute.
    """
    db_path = Path(database_name)
    if not db_path.exists():
        return {"success": False, "error": f"Database '{database_name}' not found."}

    try:
        conn = sqlite3.connect(str(db_path))
        cursor = conn.cursor()
        cursor.execute(query)
        rows = cursor.fetchall()
        columns = [desc[0] for desc in cursor.description] if cursor.description else []
        conn.close()

        return {
            "success": True,
            "output": {
                "columns": columns,
                "rows": rows[:50],
                "total_rows": len(rows),
            },
        }
    except Exception as ex:
        return {"success": False, "error": f"SQL Error: {ex}"}
```

### Optional module-level hooks

| Hook | When it runs | Contract |
| :--- | :--- | :--- |
| `TOOL_LIBRARY_NAME` / `TOOL_LIBRARY_DESC` / `TOOL_LIBRARY_ICON` | Never executed; metadata for host UIs and mounting UIs. | Module-level string constants. |
| `init_tools_library(config)` | Once per process, at lazy module load (first tool execution or dynamic prompt call). | Receives the host config dict **positionally if the signature accepts positional args, otherwise called with no arguments** (detected via `inspect.signature`). |
| `set_confirm_handler(handler)` | At lazy load, and re-invoked whenever the host registers a new handler. | Receives the host confirmation callback. |
| `tool_<name>_prompt()` | At spec-build time (`to_chat_tool_specs` / `discover_tools`). | The name must be exactly `tool_` + the base tool name + `_prompt` (e.g. for `tool_execute_query`, the prompt fn is **`tool_execute_query_prompt`** — note the full tool name including prefix). Its return value **replaces** the tool's LLM-facing description. The function itself is never registered as a tool. |

---

## 4. Schema Generation Rules (Exact Semantics)

For each `tool_*` function, the binding builds `{name, description, input_schema}` via AST:

1. **Description** = the **first paragraph** of the docstring (text before the first blank line). If no docstring: `"No description provided."`.
2. **Parameter descriptions** come from docstring lines matching Google/`Args:`-style patterns:

From ParisNeo/lollms_client, Apache-2.0.

More from ParisNeo/lollms_client

Signals

Stars
13
Forks
5
Open issues
0
Last commit (this folder)
Oct 12, 2026 (16 days ago)
Last release
Mar 5, 2025 (1 year ago)

From GitHub (fetched Oct 24, 2026).

Details

License
Apache-2.0
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from ParisNeo

Cite this page

lcp_tool_development skill. Agent Mart. https://agentmart.dev/skills/parisneo-lcp-tool-development (last updated 2026-10-24)