Skip to main content
Glama

recent_activity

Retrieve recent project activity from your knowledge management system using natural language timeframes like 'yesterday' or 'last week'.

Instructions

Get recent activity for a project or across all projects.

Timeframe supports natural language formats like:
- "2 days ago"
- "last week"
- "yesterday"
- "today"
- "3 weeks ago"
Or standard formats like "7d"

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
typeNo
depthNo
timeframeNo7d
projectNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The primary handler function for the 'recent_activity' MCP tool. It resolves the project context, fetches recent graph activity via API calls, handles discovery mode across projects or specific project mode, and formats the results into a human-readable markdown summary with stats, key items, and guidance.
    @mcp.tool(
        description="""Get recent activity for a project or across all projects.
    
        Timeframe supports natural language formats like:
        - "2 days ago"
        - "last week"
        - "yesterday"
        - "today"
        - "3 weeks ago"
        Or standard formats like "7d"
        """,
    )
    async def recent_activity(
        type: Union[str, List[str]] = "",
        depth: int = 1,
        timeframe: TimeFrame = "7d",
        project: Optional[str] = None,
        context: Context | None = None,
    ) -> str:
        """Get recent activity for a specific project or across all projects.
    
        Project Resolution:
        The server resolves projects in this order:
        1. Single Project Mode - server constrained to one project, parameter ignored
        2. Explicit project parameter - specify which project to query
        3. Default project - server configured default if no project specified
    
        Discovery Mode:
        When no specific project can be resolved, returns activity across all projects
        to help discover available projects and their recent activity.
    
        Project Discovery (when project is unknown):
        1. Call list_memory_projects() to see available projects
        2. Or use this tool without project parameter to see cross-project activity
        3. Ask the user which project to focus on
        4. Remember their choice for the conversation
    
        Args:
            type: Filter by content type(s). Can be a string or list of strings.
                Valid options:
                - "entity" or ["entity"] for knowledge entities
                - "relation" or ["relation"] for connections between entities
                - "observation" or ["observation"] for notes and observations
                Multiple types can be combined: ["entity", "relation"]
                Case-insensitive: "ENTITY" and "entity" are treated the same.
                Default is an empty string, which returns all types.
            depth: How many relation hops to traverse (1-3 recommended)
            timeframe: Time window to search. Supports natural language:
                - Relative: "2 days ago", "last week", "yesterday"
                - Points in time: "2024-01-01", "January 1st"
                - Standard format: "7d", "24h"
            project: Project name to query. Optional - server will resolve using the
                    hierarchy above. If unknown, use list_memory_projects() to discover
                    available projects.
            context: Optional FastMCP context for performance caching.
    
        Returns:
            Human-readable summary of recent activity. When no specific project is
            resolved, returns cross-project discovery information. When a specific
            project is resolved, returns detailed activity for that project.
    
        Examples:
            # Cross-project discovery mode
            recent_activity()
            recent_activity(timeframe="yesterday")
    
            # Project-specific activity
            recent_activity(project="work-docs", type="entity", timeframe="yesterday")
            recent_activity(project="research", type=["entity", "relation"], timeframe="today")
            recent_activity(project="notes", type="entity", depth=2, timeframe="2 weeks ago")
    
        Raises:
            ToolError: If project doesn't exist or type parameter contains invalid values
    
        Notes:
            - Higher depth values (>3) may impact performance with large result sets
            - For focused queries, consider using build_context with a specific URI
            - Max timeframe is 1 year in the past
        """
        track_mcp_tool("recent_activity")
        async with get_client() as client:
            # Build common parameters for API calls
            params = {
                "page": 1,
                "page_size": 10,
                "max_related": 10,
            }
            if depth:
                params["depth"] = depth
            if timeframe:
                params["timeframe"] = timeframe  # pyright: ignore
    
            # Validate and convert type parameter
            if type:
                # Convert single string to list
                if isinstance(type, str):
                    type_list = [type]
                else:
                    type_list = type
    
                # Validate each type against SearchItemType enum
                validated_types = []
                for t in type_list:
                    try:
                        # Try to convert string to enum
                        if isinstance(t, str):
                            validated_types.append(SearchItemType(t.lower()))
                    except ValueError:
                        valid_types = [t.value for t in SearchItemType]
                        raise ValueError(f"Invalid type: {t}. Valid types are: {valid_types}")
    
                # Add validated types to params
                params["type"] = [t.value for t in validated_types]  # pyright: ignore
    
            # Resolve project parameter using the three-tier hierarchy
            # allow_discovery=True enables Discovery Mode, so a project is not required
            resolved_project = await resolve_project_parameter(project, allow_discovery=True)
    
            if resolved_project is None:
                # Discovery Mode: Get activity across all projects
                logger.info(
                    f"Getting recent activity across all projects: type={type}, depth={depth}, timeframe={timeframe}"
                )
    
                # Get list of all projects
                response = await call_get(client, "/projects/projects")
                project_list = ProjectList.model_validate(response.json())
    
                projects_activity = {}
                total_items = 0
                total_entities = 0
                total_relations = 0
                total_observations = 0
                most_active_project = None
                most_active_count = 0
                active_projects = 0
    
                # Query each project's activity
                for project_info in project_list.projects:
                    project_activity = await _get_project_activity(client, project_info, params, depth)
                    projects_activity[project_info.name] = project_activity
    
                    # Aggregate stats
                    item_count = project_activity.item_count
                    if item_count > 0:
                        active_projects += 1
                        total_items += item_count
    
                        # Count by type
                        for result in project_activity.activity.results:
                            if result.primary_result.type == "entity":
                                total_entities += 1
                            elif result.primary_result.type == "relation":
                                total_relations += 1
                            elif result.primary_result.type == "observation":
                                total_observations += 1
    
                        # Track most active project
                        if item_count > most_active_count:
                            most_active_count = item_count
                            most_active_project = project_info.name
    
                # Build summary stats
                summary = ActivityStats(
                    total_projects=len(project_list.projects),
                    active_projects=active_projects,
                    most_active_project=most_active_project,
                    total_items=total_items,
                    total_entities=total_entities,
                    total_relations=total_relations,
                    total_observations=total_observations,
                )
    
                # Generate guidance for the assistant
                guidance_lines = ["\n" + "─" * 40]
    
                if active_projects == 0:
                    # No recent activity
                    guidance_lines.extend(
                        [
                            "No recent activity found in any project.",
                            "Consider: Ask which project to use or if they want to create a new one.",
                        ]
                    )
                else:
                    # At least one project has activity: suggest the most active project.
                    suggested_project = most_active_project or next(
                        (name for name, activity in projects_activity.items() if activity.item_count > 0),
                        None,
                    )
                    if suggested_project:
                        suffix = (
                            f"(most active with {most_active_count} items)" if most_active_count > 0 else ""
                        )
                        guidance_lines.append(f"Suggested project: '{suggested_project}' {suffix}".strip())
                        if active_projects == 1:
                            guidance_lines.append(f"Ask user: 'Should I use {suggested_project} for this task?'")
                        else:
                            guidance_lines.append(
                                f"Ask user: 'Should I use {suggested_project} for this task, or would you prefer a different project?'"
                            )
    
                guidance_lines.extend(
                    [
                        "",
                        "Session reminder: Remember their project choice throughout this conversation.",
                    ]
                )
    
                guidance = "\n".join(guidance_lines)
    
                # Format discovery mode output
                return _format_discovery_output(projects_activity, summary, timeframe, guidance)
    
            else:
                # Project-Specific Mode: Get activity for specific project
                logger.info(
                    f"Getting recent activity from project {resolved_project}: type={type}, depth={depth}, timeframe={timeframe}"
                )
    
                active_project = await get_active_project(client, resolved_project, context)
    
                response = await call_get(
                    client,
                    f"/v2/projects/{active_project.external_id}/memory/recent",
                    params=params,
                )
                activity_data = GraphContext.model_validate(response.json())
    
                # Format project-specific mode output
                return _format_project_output(resolved_project, activity_data, timeframe, type)
  • The import statement in tools/__init__.py that registers the recent_activity tool with the FastMCP server instance by executing the module which defines @mcp.tool()-decorated function.
    from basic_memory.mcp.tools.recent_activity import recent_activity
Behavior2/5

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

No annotations are provided, so the description carries full burden. It discloses that the tool retrieves ('Get') data, implying it's read-only, but doesn't mention behavioral traits like pagination, rate limits, authentication needs, error handling, or what 'recent activity' includes (e.g., events, changes). The timeframe format details are helpful but insufficient for a mutation-free tool with zero annotation coverage.

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

Conciseness5/5

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

The description is appropriately sized and front-loaded: the first sentence states the core purpose, followed by specific timeframe examples. Every sentence earns its place by adding value—no redundancy or waste. The bullet-point list for timeframe formats is structured for clarity without verbosity.

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

Completeness3/5

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

Given 4 parameters with 0% schema coverage and no annotations, the description is incomplete. It partially covers 'timeframe' but omits details for 'type,' 'depth,' and 'project.' An output schema exists, so return values needn't be explained, but for a read operation with multiple inputs, more parameter guidance is needed to be fully helpful.

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

Parameters3/5

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

Schema description coverage is 0%, so the description must compensate. It adds meaning for 'timeframe' by providing natural language and standard format examples, which clarifies semantics beyond the schema's title. However, it doesn't explain 'type,' 'depth,' or 'project' parameters, leaving three of four parameters with minimal guidance. The baseline is lowered due to incomplete compensation.

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

Purpose4/5

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

The description clearly states the tool's purpose: 'Get recent activity for a project or across all projects.' It uses a specific verb ('Get') and resource ('recent activity'), and distinguishes between project-specific and all-projects scope. However, it doesn't explicitly differentiate from sibling tools like 'search' or 'fetch' that might also retrieve activity data.

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

Usage Guidelines3/5

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

The description implies usage context by mentioning 'for a project or across all projects,' suggesting this tool is for retrieving activity logs. However, it provides no explicit guidance on when to use this versus alternatives like 'search' or 'fetch,' nor does it mention prerequisites or exclusions. The timeframe examples offer some operational context but not comparative guidance.

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/basicmachines-co/basic-memory'

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