
Search Content
- 1 installs
- 1 repo stars
- Updated April 10, 2026
- ericfisherdev/claude-plugins
Searches Confluence content via a CQL-based Python script, filtering by space, type, label, contributor, and modified date.
About
Searches Confluence content through a bundled Python script using CQL, with token-efficient output and parent-hierarchy info. A developer uses it to find pages, blog posts, or comments across Confluence during content discovery.
- CQL-based Confluence search via a Python script
- Filters by space, type, label, contributor, and modified date
Search Content by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,361 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Jul 8, 2026 (Skillselion catalog sync)
npx skills add https://github.com/ericfisherdev/claude-plugins --skill search-contentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 1 |
| Last updated | April 10, 2026 |
| Repository | ericfisherdev/claude-plugins ↗ |
What it does
Searches Confluence content via a CQL-based Python script, filtering by space, type, label, contributor, and modified date.
Files
Search Confluence Content
IMPORTANT: Always use this skill's Python script for searching Confluence. This skill provides CQL-based search with token-efficient output, including parent hierarchy information.
Quick Start
Use the Python script at scripts/search_confluence.py:
# Simple text search
python scripts/search_confluence.py "authentication"
# Search in specific space
python scripts/search_confluence.py "API documentation" --space DEV
# Search with type filter
python scripts/search_confluence.py "meeting notes" --type blogpost
# Search by label
python scripts/search_confluence.py --label "architecture"Options
| Option | Description |
|---|---|
query | Search text (positional argument) |
--space, -s | Limit search to specific space |
--type, -t | Content type: page, blogpost, comment |
--label, -l | Search for content with specific label |
--contributor | Search by content contributor |
--modified-after | Content modified after date (YYYY-MM-DD) |
--modified-before | Content modified before date (YYYY-MM-DD) |
--limit | Maximum results (default: 25) |
--format, -f | Output: compact (default), text, json |
Search Types
Text Search
Search in page titles and content:
python scripts/search_confluence.py "deployment guide"Label Search
Find pages with specific labels:
python scripts/search_confluence.py --label "api-reference"Combined Search
python scripts/search_confluence.py "authentication" --space DEV --label "security"CQL (Confluence Query Language)
This skill uses CQL internally. Advanced users can leverage CQL patterns:
| Search Pattern | CQL Equivalent |
|---|---|
| Text search | text ~ "query" |
| Space filter | space = "KEY" |
| Type filter | type = page |
| Label filter | label = "name" |
| Date filter | lastModified >= "2024-01-01" |
Common Workflows
Find Documentation
# Find all API-related docs
python scripts/search_confluence.py "API" --space DEV --type page
# Find recent changes
python scripts/search_confluence.py "" --space DEV --modified-after 2024-01-01Find Meeting Notes
python scripts/search_confluence.py "meeting notes" --type blogpost --limit 10Find by Label
# Find all architecture decisions
python scripts/search_confluence.py --label "adr"
# Find deprecated content
python scripts/search_confluence.py --label "deprecated" --space DEVFind Contributor's Content
python scripts/search_confluence.py --contributor "john.smith" --space DEVOutput Formats
compact (default):
SEARCH|5|"authentication"
HIT|123456|Authentication Guide|DEV|page|parent:Security Docs
HIT|123457|OAuth Setup|DEV|page|parent:Authentication Guide
HIT|123458|SSO Integration|DEV|page|parent:Authentication Guide
HIT|123459|Security Best Practices|DEV|page
HIT|123460|Login Flow|DEV|page|parent:User ManagementNote: Parent information is included when available (e.g., parent:Security Docs).
text:
Search Results: "authentication" (5 found)
1. Authentication Guide
ID: 123456 | Space: DEV | Type: page | Parent: Security Docs
URL: https://yoursite.atlassian.net/wiki/spaces/DEV/pages/123456
2. OAuth Setup
ID: 123457 | Space: DEV | Type: page | Parent: Authentication Guide
URL: https://yoursite.atlassian.net/wiki/spaces/DEV/pages/123457
...json:
{
"query": "authentication",
"count": 5,
"results": [
{
"id": "123456",
"title": "Authentication Guide",
"space": "DEV",
"type": "page",
"url": "...",
"parentId": "123400",
"parentTitle": "Security Docs"
}
]
}Parent Information
Search results now include parent/hierarchy information when available:
- parentId - ID of the direct parent page or folder
- parentTitle - Title of the direct parent
This helps understand where each result is located in the content hierarchy.
Environment Setup
Requires environment variables:
CONFLUENCE_BASE_URL- e.g.,https://yoursite.atlassian.netCONFLUENCE_EMAIL- Your Atlassian account emailCONFLUENCE_API_TOKEN- API token from Atlassian account settings
Reference
For detailed options, see references/options-reference.md.
Search Content - Options Reference
Arguments
At least one search criterion is required.
| Argument | Type | Description |
|---|---|---|
query | string | Search text (positional, optional) |
Optional Arguments
| Option | Short | Type | Default | Description |
|---|---|---|---|---|
--space | -s | string | - | Limit search to specific space |
--type | -t | choice | - | Content type: page, blogpost, comment |
--label | -l | string | - | Search for content with specific label |
--contributor | - | string | - | Search by content contributor |
--modified-after | - | date | - | Content modified after date (YYYY-MM-DD) |
--modified-before | - | date | - | Content modified before date (YYYY-MM-DD) |
--limit | - | int | 25 | Maximum results |
--format | -f | choice | compact | Output format: compact, text, json |
CQL Query Building
The script builds Confluence Query Language (CQL) queries from the provided options:
| Option | CQL Translation |
|---|---|
query "text" | text ~ "text" |
--space DEV | space = "DEV" |
--type page | type = page |
--label "api" | label = "api" |
--contributor "user" | contributor = "user" |
--modified-after 2024-01-01 | lastModified >= "2024-01-01" |
--modified-before 2024-12-31 | lastModified <= "2024-12-31" |
Multiple options are combined with AND.
Content Types
| Type | Description |
|---|---|
page | Standard Confluence pages |
blogpost | Blog posts |
comment | Comments on pages/posts |
Output Format Details
compact
SEARCH|{count}|"{query}"
HIT|{id}|{title}|{space}|{type}
HIT|{id}|{title}|{space}|{type}
...text
Search Results: "{query}" ({count} found)
1. {title}
ID: {id} | Space: {space} | Type: {type}
URL: {url}
2. {title}
ID: {id} | Space: {space} | Type: {type}
URL: {url}
...json
{
"query": "search text",
"count": 5,
"results": [
{
"id": "123456",
"title": "Page Title",
"space": "SPACEKEY",
"type": "page",
"url": "https://..."
}
]
}Search Examples
Basic Text Search
python search_confluence.py "authentication"Space-Scoped Search
python search_confluence.py "API" --space DEVLabel Search
python search_confluence.py --label "architecture"Combined Filters
python search_confluence.py "security" \
--space DEV \
--type page \
--label "reviewed" \
--modified-after 2024-01-01Find All Pages in Space
python search_confluence.py --space DEV --type page --limit 100Recent Changes
python search_confluence.py --space DEV --modified-after 2024-01-01Contributor Search
python search_confluence.py --contributor "john.smith" --type pageSearch Tips
1. Exact Phrases: Use quotes for exact matching
python search_confluence.py '"authentication flow"'2. Wildcards: CQL supports wildcards
auth*matches "authentication", "authorization", etc.
3. Boolean Logic: Multiple criteria use AND
- To find pages with EITHER label, run separate searches
4. Date Ranges: Combine --modified-after and --modified-before
python search_confluence.py --space DEV \
--modified-after 2024-01-01 \
--modified-before 2024-06-30Error Codes
| Exit Code | Meaning |
|---|---|
| 0 | Success (even if no results) |
| 1 | Error - API error, authentication failed |
Limitations
- Maximum 25 results by default (configurable with
--limit) - CQL has a complexity limit on Confluence Cloud
- Some advanced CQL features may not be available on all versions
Environment Variables
| Variable | Required | Description |
|---|---|---|
CONFLUENCE_BASE_URL | Yes | Confluence instance URL |
CONFLUENCE_EMAIL | Yes | Atlassian account email |
CONFLUENCE_API_TOKEN | Yes | API token from Atlassian account settings |
#!/usr/bin/env python3
"""
Search Confluence content with token-efficient output.
Usage:
python search_confluence.py QUERY [options]
python search_confluence.py --label LABEL [options]
Examples:
python search_confluence.py "authentication"
python search_confluence.py "API docs" --space DEV
python search_confluence.py --label "architecture"
"""
import argparse
import json
import os
import sys
from pathlib import Path
from urllib.parse import quote
# Add shared module to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "shared"))
from confluence_cache import ConfluenceCache
def build_cql(
query: str | None,
space: str | None,
content_type: str | None,
label: str | None,
contributor: str | None,
modified_after: str | None,
modified_before: str | None
) -> str:
"""Build CQL query string from parameters."""
conditions = []
if query:
# Escape quotes in query
escaped_query = query.replace('"', '\\"')
conditions.append(f'text ~ "{escaped_query}"')
if space:
conditions.append(f'space = "{space}"')
if content_type:
conditions.append(f'type = {content_type}')
if label:
conditions.append(f'label = "{label}"')
if contributor:
conditions.append(f'contributor = "{contributor}"')
if modified_after:
conditions.append(f'lastModified >= "{modified_after}"')
if modified_before:
conditions.append(f'lastModified <= "{modified_before}"')
# Default to pages if no conditions
if not conditions:
conditions.append("type = page")
return " AND ".join(conditions)
def format_compact(results: list[dict], query: str | None, base_url: str) -> str:
"""Format search results as compact output."""
lines = []
query_display = f'"{query}"' if query else "(all)"
lines.append(f"SEARCH|{len(results)}|{query_display}")
for r in results:
space = r.get("space", "")
content_type = r.get("type", "page")
parent = r.get("parentTitle", "")
# Include parent in output if available
if parent:
lines.append(f"HIT|{r['id']}|{r['title']}|{space}|{content_type}|parent:{parent}")
else:
lines.append(f"HIT|{r['id']}|{r['title']}|{space}|{content_type}")
return "\n".join(lines)
def format_text(results: list[dict], query: str | None, base_url: str) -> str:
"""Format search results as readable text."""
lines = []
query_display = f'"{query}"' if query else "(all)"
lines.append(f"Search Results: {query_display} ({len(results)} found)")
lines.append("")
for i, r in enumerate(results, 1):
space = r.get("space", "")
content_type = r.get("type", "page")
url = r.get("url", f"{base_url}/wiki/spaces/{space}/pages/{r['id']}")
parent = r.get("parentTitle", "")
lines.append(f"{i}. {r['title']}")
info_line = f" ID: {r['id']} | Space: {space} | Type: {content_type}"
if parent:
info_line += f" | Parent: {parent}"
lines.append(info_line)
lines.append(f" URL: {url}")
lines.append("")
return "\n".join(lines)
def format_json(results: list[dict], query: str | None, base_url: str) -> str:
"""Format search results as JSON."""
output = {
"query": query or "",
"count": len(results),
"results": results
}
return json.dumps(output, indent=2)
def main():
parser = argparse.ArgumentParser(
description="Search Confluence content"
)
parser.add_argument(
"query",
nargs="?",
help="Search text"
)
parser.add_argument(
"--space", "-s",
help="Limit search to specific space"
)
parser.add_argument(
"--type", "-t",
choices=["page", "blogpost", "comment"],
help="Content type filter"
)
parser.add_argument(
"--label", "-l",
help="Search for content with specific label"
)
parser.add_argument(
"--contributor",
help="Search by content contributor"
)
parser.add_argument(
"--modified-after",
help="Content modified after date (YYYY-MM-DD)"
)
parser.add_argument(
"--modified-before",
help="Content modified before date (YYYY-MM-DD)"
)
parser.add_argument(
"--limit",
type=int,
default=25,
help="Maximum results (default: 25)"
)
parser.add_argument(
"--format", "-f",
choices=["compact", "text", "json"],
default="compact",
help="Output format (default: compact)"
)
args = parser.parse_args()
# Validate - need at least one search criterion
if not args.query and not args.label and not args.space and not args.contributor:
parser.error("At least one search criterion required (query, --label, --space, or --contributor)")
# Check environment
base_url = os.environ.get("CONFLUENCE_BASE_URL", "")
if not base_url:
print("ERROR: CONFLUENCE_BASE_URL environment variable required", file=sys.stderr)
sys.exit(1)
# Initialize cache
cache = ConfluenceCache()
try:
# Build CQL query
cql = build_cql(
args.query,
args.space,
args.type,
args.label,
args.contributor,
args.modified_after,
args.modified_before
)
# Execute search using v1 API (CQL search)
# Include ancestors for parent info
encoded_cql = quote(cql)
result = cache._api_request(
f"/content/search?cql={encoded_cql}&limit={args.limit}&expand=ancestors,space",
api_version="v1"
)
# Process results
results = []
for r in result.get("results", []):
space_key = r.get("space", {}).get("key", "")
page_id = r["id"]
content_type = r.get("type", "page")
# Build URL based on type
if content_type == "blogpost":
url = f"{base_url}/wiki/spaces/{space_key}/blog/{page_id}"
elif content_type == "folder":
url = f"{base_url}/wiki/spaces/{space_key}/folders/{page_id}"
else:
url = f"{base_url}/wiki/spaces/{space_key}/pages/{page_id}"
# Get parent info if available
ancestors = r.get("ancestors", [])
parent_title = None
parent_id = None
if ancestors:
# Last ancestor is the direct parent
parent = ancestors[-1]
parent_title = parent.get("title", "")
parent_id = parent.get("id", "")
result_item = {
"id": page_id,
"title": r["title"],
"space": space_key,
"type": content_type,
"url": url
}
if parent_id:
result_item["parentId"] = parent_id
result_item["parentTitle"] = parent_title
results.append(result_item)
if not results:
print(f"No results found for: {args.query or '(criteria)'}", file=sys.stderr)
sys.exit(0)
# Format output
if args.format == "compact":
output = format_compact(results, args.query, base_url)
elif args.format == "text":
output = format_text(results, args.query, base_url)
else:
output = format_json(results, args.query, base_url)
print(output)
except EnvironmentError as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
except RuntimeError as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
except ConnectionError as e:
print(f"ERROR: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()