Skip to main content
Glama
list91
by list91

mcp_read

Read clipboard content from CopyQ manager tabs, including structured MCP storage and external clipboard data, with modes for browsing, listing items, retrieving full content, and searching across tabs.

Instructions

Read from CopyQ clipboard manager.

MCP tabs (full read/write access):

  • mcp/info - general information storage

  • mcp/заметки - notes storage

  • mcp/workspace - projects (supports subtabs like workspace/myproject)

External tabs (READ-ONLY access with scope="all" or scope="external"):

  • All other CopyQ tabs like "&clipboard", personal tabs, etc.

  • Use scope="all" to see all tabs, scope="external" for non-mcp only

Modes:

  • tree: Get tab structure with previews. Use FIRST to see available tabs.

  • list: Get items from tab with pagination

  • item: Get single item with full content (text, tags, note)

  • search: Search by regex across tabs

Parameters:

  • mode (required): "tree" | "list" | "item" | "search"

  • tab: For mcp tabs use relative path "info", "workspace/proj1". For external use full name "&clipboard"

  • scope: "mcp" (default) | "all" | "external" - controls which tabs are accessible

  • index: Item index (for mode=item)

  • query: Search regex (for mode=search)

Examples:

  • tree of mcp tabs: mode="tree"

  • tree of ALL tabs: mode="tree", scope="all"

  • read external tab: mode="list", tab="&clipboard", scope="external"

  • search everywhere: mode="search", query="pattern", scope="all"

Errors: TAB_NOT_FOUND, INDEX_OUT_OF_BOUNDS, INVALID_MODE

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
modeYesOperation mode
tabNoTab path: 'info', 'workspace/proj1' or full external name '&clipboard'
scopeNoTab scope: mcp (default, read/write), all (read external), external (only non-mcp, read-only)mcp
indexNoItem index (for mode=item)
queryNoSearch regex (for mode=search)
search_inNoall
max_depthNo
max_itemsNo
skipNo
include_textNo
include_tagsNo
include_noteNo

Implementation Reference

  • Main handler function for the mcp_read tool. Validates parameters and dispatches to mode-specific helper functions (_read_tree, _read_list, _read_item, _read_search).
    async def mcp_read(
        mode: str,
        tab: str = "",
        index: int | None = None,
        query: str | None = None,
        search_in: str = "all",
        scope: str = "mcp",
        max_depth: int = 2,
        max_items: int = 20,
        skip: int = 0,
        include_text: bool = True,
        include_tags: bool = True,
        include_note: bool = False,
    ) -> str:
        """
        Universal read operation for CopyQ MCP.
    
        Modes:
        - tree: Get tab structure with metadata
        - list: Get items from a specific tab
        - item: Get single item fully
        - search: Search across tabs
    
        Scope:
        - mcp: Only mcp/* tabs (default, full access)
        - all: All CopyQ tabs (external tabs are read-only)
        - external: Only non-mcp tabs (read-only)
    
        Returns compact pipe-separated format.
        """
        try:
            # Validate mode
            valid_modes = [m.value for m in ReadMode]
            if mode not in valid_modes:
                raise InvalidModeError(mode, valid_modes)
    
            # Validate scope
            valid_scopes = [s.value for s in Scope]
            if scope not in valid_scopes:
                raise MCPCopyQError(ErrorCode.INVALID_PARAM, f"Invalid scope '{scope}'. Valid: {', '.join(valid_scopes)}")
    
            if mode == "tree":
                return await _read_tree(max_depth, max_items, include_text, include_tags, scope)
    
            elif mode == "list":
                if not tab:
                    raise MissingParamError("tab", mode)
                return await _read_list(
                    tab, max_items, skip, include_text, include_tags, include_note, scope
                )
    
            elif mode == "item":
                if not tab:
                    raise MissingParamError("tab", mode)
                if index is None:
                    raise MissingParamError("index", mode)
                return await _read_item(tab, index, include_text, include_tags, include_note, scope)
    
            elif mode == "search":
                if not query:
                    raise MissingParamError("query", mode)
                return await _read_search(
                    query, search_in, max_items, skip, include_text, include_tags, scope
                )
    
            return "error|INVALID_MODE|Unknown mode"
    
        except MCPCopyQError as e:
            return e.to_response()
        except Exception as e:
            return f"error|INTERNAL|{str(e)}"
  • Tool registration in @server.list_tools(), defining name, description, and JSON inputSchema for mcp_read.
            Tool(
                name="mcp_read",
                description="""Read from CopyQ clipboard manager.
    
    MCP tabs (full read/write access):
    - mcp/info - general information storage
    - mcp/заметки - notes storage
    - mcp/workspace - projects (supports subtabs like workspace/myproject)
    
    External tabs (READ-ONLY access with scope="all" or scope="external"):
    - All other CopyQ tabs like "&clipboard", personal tabs, etc.
    - Use scope="all" to see all tabs, scope="external" for non-mcp only
    
    Modes:
    - tree: Get tab structure with previews. Use FIRST to see available tabs.
    - list: Get items from tab with pagination
    - item: Get single item with full content (text, tags, note)
    - search: Search by regex across tabs
    
    Parameters:
    - mode (required): "tree" | "list" | "item" | "search"
    - tab: For mcp tabs use relative path "info", "workspace/proj1". For external use full name "&clipboard"
    - scope: "mcp" (default) | "all" | "external" - controls which tabs are accessible
    - index: Item index (for mode=item)
    - query: Search regex (for mode=search)
    
    Examples:
    - tree of mcp tabs: mode="tree"
    - tree of ALL tabs: mode="tree", scope="all"
    - read external tab: mode="list", tab="&clipboard", scope="external"
    - search everywhere: mode="search", query="pattern", scope="all"
    
    Errors: TAB_NOT_FOUND, INDEX_OUT_OF_BOUNDS, INVALID_MODE""",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "mode": {
                            "type": "string",
                            "enum": ["tree", "list", "item", "search"],
                            "description": "Operation mode"
                        },
                        "tab": {
                            "type": "string",
                            "description": "Tab path: 'info', 'workspace/proj1' or full external name '&clipboard'"
                        },
                        "scope": {
                            "type": "string",
                            "enum": ["mcp", "all", "external"],
                            "default": "mcp",
                            "description": "Tab scope: mcp (default, read/write), all (read external), external (only non-mcp, read-only)"
                        },
                        "index": {
                            "type": "integer",
                            "description": "Item index (for mode=item)"
                        },
                        "query": {
                            "type": "string",
                            "description": "Search regex (for mode=search)"
                        },
                        "search_in": {
                            "type": "string",
                            "enum": ["text", "note", "tags", "all"],
                            "default": "all"
                        },
                        "max_depth": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 10,
                            "default": 2
                        },
                        "max_items": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        },
                        "skip": {
                            "type": "integer",
                            "minimum": 0,
                            "default": 0
                        },
                        "include_text": {
                            "type": "boolean",
                            "default": True
                        },
                        "include_tags": {
                            "type": "boolean",
                            "default": True
                        },
                        "include_note": {
                            "type": "boolean",
                            "default": False
                        }
                    },
                    "required": ["mode"]
                }
            ),
  • Pydantic model defining input parameters and validation for mcp_read.
    class ReadRequest(BaseModel):
        """Parameters for mcp_read tool."""
    
        mode: ReadMode = Field(..., description="Operation mode: tree, list, item, search")
        tab: str = Field(default="", description="Tab path: 'info', 'workspace/proj1' or full path like '&clipboard'")
        index: int | None = Field(default=None, description="Item index (for mode=item)")
        query: str | None = Field(default=None, description="Search query regex (for mode=search)")
        search_in: SearchIn = Field(default=SearchIn.ALL, description="Where to search")
        scope: Scope = Field(default=Scope.MCP, description="Tab scope: mcp (default), all, external")
        max_depth: int = Field(default=2, ge=1, le=10, description="Max depth for tree")
        max_items: int = Field(default=20, ge=1, le=100, description="Max items to return")
        skip: int = Field(default=0, ge=0, description="Skip N items (pagination)")
        include_text: bool = Field(default=True, description="Include text/preview")
        include_tags: bool = Field(default=True, description="Include tags")
        include_note: bool = Field(default=False, description="Include note")
  • Dispatch from MCP server's call_tool handler to the mcp_read function.
    if name == "mcp_read":
        result = await mcp_read(**arguments)
    elif name == "mcp_write":
  • Helper function for 'tree' mode, building hierarchical tab structure with previews.
    async def _read_tree(
        max_depth: int,
        max_items: int,
        include_text: bool,
        include_tags: bool,
        scope: str = "mcp",
    ) -> str:
        """Build tree structure of tabs."""
        # Get tabs based on scope
        if scope == "mcp":
            tabs = await client.list_tabs()
        elif scope == "external":
            tabs = await client.list_external_tabs()
        else:  # "all"
            tabs = await client.list_all_tabs()
    
        lines = []
    
        async def format_tab(
            path: str,
            depth: int,
            indent: str = "",
            is_external: bool = False
        ) -> None:
            if depth > max_depth:
                return
    
            count = await client.get_count(path, external=is_external)
    
            # Clean path for display
            if path.startswith(f"{MCP_ROOT}/"):
                display_path = path[len(MCP_ROOT) + 1:]
            else:
                display_path = path
    
            # Mark external tabs
            external_marker = " [external]" if is_external else ""
            lines.append(f"{indent}{display_path} [{count}]{external_marker}")
    
            # Add preview items
            preview_count = min(count, max_items)
            for i in range(preview_count):
                preview = await client.read_item_preview(path, i, external=is_external)
    
                parts = [f"{indent}  {i}:"]
                if include_text:
                    parts.append(f'"{preview["text_preview"]}"')
                if include_tags and preview["tags"]:
                    parts.append(f"[{','.join(preview['tags'])}]")
                if preview["has_note"]:
                    parts.append("+note")
    
                lines.append("|".join(parts) if len(parts) > 1 else parts[0])
    
            # Find child tabs
            child_tabs = [t for t in tabs if t.startswith(path + "/") and t.count("/") == path.count("/") + 1]
            for child in child_tabs:
                child_is_external = not child.startswith(f"{MCP_ROOT}/")
                await format_tab(child, depth + 1, indent + "  ", child_is_external)
    
        if scope == "mcp":
            # Start from mcp root tabs
            for root in ALLOWED_ROOT_TABS:
                full_path = f"{MCP_ROOT}/{root}"
                if full_path in tabs or any(t.startswith(full_path + "/") for t in tabs):
                    await format_tab(full_path, 1, "", False)
        else:
            # For all/external scope, show root-level tabs
            root_tabs = [t for t in tabs if "/" not in t or (t.count("/") == 1 and t.startswith(f"{MCP_ROOT}/"))]
    
            # Also get direct children of mcp/ for "all" scope
            if scope == "all":
                # Get unique root tabs (either no "/" or first level of mcp/)
                seen_roots = set()
                for t in tabs:
                    if t.startswith(f"{MCP_ROOT}/"):
                        # For mcp tabs, get first level after mcp/
                        parts = t.split("/")
                        if len(parts) >= 2:
                            seen_roots.add(f"{MCP_ROOT}/{parts[1]}")
                    else:
                        # For external tabs, get root
                        root = t.split("/")[0]
                        seen_roots.add(root)
                root_tabs = sorted(seen_roots)
    
            for tab in root_tabs:
                is_external = not tab.startswith(f"{MCP_ROOT}/")
                await format_tab(tab, 1, "", is_external)
    
        return "\n".join(lines) if lines else "empty|no tabs found"
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes access permissions (read/write for MCP tabs, read-only for external tabs), error conditions (TAB_NOT_FOUND, INDEX_OUT_OF_BOUNDS, INVALID_MODE), and operational modes. However, it doesn't mention rate limits, performance characteristics, or authentication requirements, leaving some behavioral aspects uncovered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (MCP tabs, External tabs, Modes, Parameters, Examples, Errors) and front-loaded essential information. While comprehensive, it could be slightly more concise by integrating some parameter details more tightly, but overall it's efficient with minimal wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (12 parameters, multiple modes, access rules) and lack of annotations/output schema, the description is largely complete, covering purpose, usage, parameters, and errors. However, it doesn't detail the return format or pagination behavior for list mode, and some parameters lack semantic explanation, leaving minor gaps for a tool of this complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only 42% schema description coverage, the description compensates well by explaining the semantics of key parameters like mode, tab, scope, index, and query, including examples and usage context. It adds significant value beyond the schema, though it doesn't cover all 12 parameters (e.g., search_in, max_depth, include_text are mentioned only in schema).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool reads from the CopyQ clipboard manager, specifying the verb 'read' and resource 'CopyQ clipboard manager'. It distinguishes from siblings by focusing on read operations (vs. mcp_write for writing and mcp_validate for validation), making the purpose specific and differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool, including detailed access rules for MCP vs. external tabs, scope options, and mode-specific use cases. It distinguishes from alternatives by specifying read-only access for external tabs and directing users to use specific modes for different tasks, with clear examples for each scenario.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/list91/mcp-copyq'

If you have feedback or need assistance with the MCP directory API, please join our Discord server