Skip to main content
Glama

get_project_context

Before starting work, retrieve a condensed project context snapshot: recent sessions, favorites, and stats to load memory.

Instructions

Get a condensed context snapshot for the current project. Call this at the START of every session to load project memory: recent sessions, favorited sessions, and project stats. This is your primary memory tool — use it before doing any work.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectIdNoProject ID (defaults to current project)
detailNoDetail level for token budgeting: "minimal" (~500 tokens), "standard" (~1500 tokens, default), "full" (~3000+ tokens)standard

Implementation Reference

  • Tool registration via server.tool() call with name 'get_project_context', description, Zod schema (projectId, detail params), and handler function.
    // ─── get_project_context ────────────────────────────────────────
    server.tool(
      'get_project_context',
      'Get a condensed context snapshot for the current project. Call this at the START of every session to load project memory: recent sessions, favorited sessions, and project stats. This is your primary memory tool — use it before doing any work.',
      {
        projectId: z.string().optional().describe('Project ID (defaults to current project)'),
        detail: z.enum(['minimal', 'standard', 'full']).default('standard').optional().describe('Detail level for token budgeting: "minimal" (~500 tokens), "standard" (~1500 tokens, default), "full" (~3000+ tokens)'),
      },
      async ({ projectId, detail = 'standard' }) => {
        try {
          const { fileScanner, metadataService, memoryService, sessionParser, currentProjectId } = await getServices()
          const { project, projectId: resolvedId } = await resolveProject(fileScanner, projectId, currentProjectId)
    
          const sessions = await fileScanner.scanSessions(project.path)
          sessions.sort((a, b) => new Date(b.lastUpdatedAt) - new Date(a.lastUpdatedAt))
    
          // ── minimal: just project stats + session titles ──
          if (detail === 'minimal') {
            const titles = sessions.slice(0, 5).map(s => s.title || s.sessionId)
            const memStats = await memoryService.getStats(resolvedId)
            return {
              content: [{
                type: 'text',
                text: JSON.stringify({
                  project: project.name,
                  sessions: sessions.length,
                  lastActivity: sessions[0]?.lastUpdatedAt || null,
                  recentTitles: titles,
                  storedMemories: memStats.total,
                }),
              }],
            }
          }
    
          // ── standard + full: recent sessions with metadata ──
          const sessionCount = detail === 'full' ? 10 : 5
          const recentSessions = await Promise.all(
            sessions.slice(0, sessionCount).map(async (s) => {
              const meta = await metadataService.getMetadata(resolvedId, s.sessionId)
              let topicHint = null
              if (detail === 'full') {
                try {
                  const parsed = await sessionParser.parseSession(s.filePath)
                  const firstUser = parsed.messages.find(m => m.role === 'user')
                  if (firstUser) {
                    const content = typeof firstUser.content === 'string'
                      ? firstUser.content
                      : JSON.stringify(firstUser.content)
                    topicHint = content.slice(0, 200).replace(/\n/g, ' ')
                  }
                } catch { /* skip parse errors */ }
              }
    
              return {
                sessionId: s.sessionId,
                title: meta?.customTitle || s.title || s.sessionId,
                messageCount: s.messageCount,
                lastUpdated: s.lastUpdatedAt,
                isFavorited: meta?.isFavorited || false,
                tags: meta?.tags || [],
                ...(topicHint ? { topicHint } : {}),
              }
            })
          )
    
          // Get favorited sessions
          const allMeta = await Promise.all(
            sessions.map(async (s) => {
              const meta = await metadataService.getMetadata(resolvedId, s.sessionId)
              return meta?.isFavorited ? {
                sessionId: s.sessionId,
                title: meta?.customTitle || s.title || s.sessionId,
                tags: meta?.tags || [],
                notes: meta?.notes || null,
              } : null
            })
          )
          const favorites = allMeta.filter(Boolean)
    
          // Get stored memories (high-priority ones for context)
          const highPriorityMemories = await memoryService.recall({
            projectId: resolvedId,
            limit: detail === 'full' ? 20 : 10,
          })
          // Filter to priority >= 7 for standard, all for full
          const memories = detail === 'full'
            ? highPriorityMemories
            : highPriorityMemories.filter(m => m.priority >= 7)
    
          const memStats = await memoryService.getStats(resolvedId)
    
          const context = {
            project: {
              id: resolvedId,
              name: project.name,
              totalSessions: sessions.length,
              lastActivity: sessions[0]?.lastUpdatedAt || null,
            },
            recentSessions,
            favoritedSessions: favorites.slice(0, 10),
            memories: memories.length > 0 ? memories : undefined,
            memoryStats: memStats,
            summary: `Project "${project.name}" has ${sessions.length} sessions, ${memStats.total} stored memories. Last activity: ${sessions[0]?.lastUpdatedAt || 'unknown'}. ${favorites.length} favorited sessions.`,
          }
    
          return {
            content: [{
              type: 'text',
              text: JSON.stringify(context, null, 2),
            }],
          }
        } catch (error) {
          return {
            content: [{ type: 'text', text: `Error getting project context: ${error.message}` }],
            isError: true,
          }
        }
      }
    )
  • Main handler function: resolves project, scans sessions, returns context based on detail level (minimal/standard/full) with session metadata, favorites, and stored memories.
    async ({ projectId, detail = 'standard' }) => {
      try {
        const { fileScanner, metadataService, memoryService, sessionParser, currentProjectId } = await getServices()
        const { project, projectId: resolvedId } = await resolveProject(fileScanner, projectId, currentProjectId)
    
        const sessions = await fileScanner.scanSessions(project.path)
        sessions.sort((a, b) => new Date(b.lastUpdatedAt) - new Date(a.lastUpdatedAt))
    
        // ── minimal: just project stats + session titles ──
        if (detail === 'minimal') {
          const titles = sessions.slice(0, 5).map(s => s.title || s.sessionId)
          const memStats = await memoryService.getStats(resolvedId)
          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                project: project.name,
                sessions: sessions.length,
                lastActivity: sessions[0]?.lastUpdatedAt || null,
                recentTitles: titles,
                storedMemories: memStats.total,
              }),
            }],
          }
        }
    
        // ── standard + full: recent sessions with metadata ──
        const sessionCount = detail === 'full' ? 10 : 5
        const recentSessions = await Promise.all(
          sessions.slice(0, sessionCount).map(async (s) => {
            const meta = await metadataService.getMetadata(resolvedId, s.sessionId)
            let topicHint = null
            if (detail === 'full') {
              try {
                const parsed = await sessionParser.parseSession(s.filePath)
                const firstUser = parsed.messages.find(m => m.role === 'user')
                if (firstUser) {
                  const content = typeof firstUser.content === 'string'
                    ? firstUser.content
                    : JSON.stringify(firstUser.content)
                  topicHint = content.slice(0, 200).replace(/\n/g, ' ')
                }
              } catch { /* skip parse errors */ }
            }
    
            return {
              sessionId: s.sessionId,
              title: meta?.customTitle || s.title || s.sessionId,
              messageCount: s.messageCount,
              lastUpdated: s.lastUpdatedAt,
              isFavorited: meta?.isFavorited || false,
              tags: meta?.tags || [],
              ...(topicHint ? { topicHint } : {}),
            }
          })
        )
    
        // Get favorited sessions
        const allMeta = await Promise.all(
          sessions.map(async (s) => {
            const meta = await metadataService.getMetadata(resolvedId, s.sessionId)
            return meta?.isFavorited ? {
              sessionId: s.sessionId,
              title: meta?.customTitle || s.title || s.sessionId,
              tags: meta?.tags || [],
              notes: meta?.notes || null,
            } : null
          })
        )
        const favorites = allMeta.filter(Boolean)
    
        // Get stored memories (high-priority ones for context)
        const highPriorityMemories = await memoryService.recall({
          projectId: resolvedId,
          limit: detail === 'full' ? 20 : 10,
        })
        // Filter to priority >= 7 for standard, all for full
        const memories = detail === 'full'
          ? highPriorityMemories
          : highPriorityMemories.filter(m => m.priority >= 7)
    
        const memStats = await memoryService.getStats(resolvedId)
    
        const context = {
          project: {
            id: resolvedId,
            name: project.name,
            totalSessions: sessions.length,
            lastActivity: sessions[0]?.lastUpdatedAt || null,
          },
          recentSessions,
          favoritedSessions: favorites.slice(0, 10),
          memories: memories.length > 0 ? memories : undefined,
          memoryStats: memStats,
          summary: `Project "${project.name}" has ${sessions.length} sessions, ${memStats.total} stored memories. Last activity: ${sessions[0]?.lastUpdatedAt || 'unknown'}. ${favorites.length} favorited sessions.`,
        }
    
        return {
          content: [{
            type: 'text',
            text: JSON.stringify(context, null, 2),
          }],
        }
      } catch (error) {
        return {
          content: [{ type: 'text', text: `Error getting project context: ${error.message}` }],
          isError: true,
        }
      }
    }
  • Input schema using Zod: projectId (optional string) and detail (enum: minimal/standard/full, default standard).
    {
      projectId: z.string().optional().describe('Project ID (defaults to current project)'),
      detail: z.enum(['minimal', 'standard', 'full']).default('standard').optional().describe('Detail level for token budgeting: "minimal" (~500 tokens), "standard" (~1500 tokens, default), "full" (~3000+ tokens)'),
    },
  • resolveProject helper function used to find project by ID or partial match, with fallback to current project.
    async function resolveProject(fileScanner, projectId, currentProjectId) {
      const targetId = projectId || currentProjectId
      const projects = await fileScanner.scanProjects()
      const project = projects.find(p => p.id === targetId)
    
      if (!project) {
        // Try partial match (user might pass just the short name)
        const partial = projects.find(p =>
          p.name === targetId || p.id.endsWith(`-${targetId}`)
        )
        if (partial) return { project: partial, projectId: partial.id }
    
        const available = projects.slice(0, 10).map(p => `  ${p.id} (${p.name})`).join('\n')
        throw new Error(
          `Project not found: "${targetId}"\n\nAvailable projects:\n${available}${projects.length > 10 ? `\n  ... and ${projects.length - 10} more` : ''}`
        )
      }
    
      return { project, projectId: targetId }
    }
  • System instructions recommending get_project_context as first call at session start to load project memory.
    '- At session start: call get_project_context to load project memory (focus, recent work, conventions)',
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. It discloses what the tool returns ('recent sessions, favorited sessions, and project stats') and mentions token budgeting via the 'detail' parameter. It does not discuss error handling or edge cases (e.g., empty projects), but it is sufficient for an agent to understand the tool's non-destructive nature and purpose.

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 just two sentences. The first sentence immediately states the purpose. The second provides usage guidance. No unnecessary words. Every sentence earns its place.

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 of a tool that loads project-wide context, the description covers what it returns and when to use it. It lacks an explicit output schema but compensates by describing the snapshot contents. It is sufficient for the agent to decide to call it at startup.

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?

Schema coverage is 100%, so baseline is 3. The description adds value by explaining the output as a 'condensed context snapshot' with specific content (sessions, stats). It also elaborates on the 'detail' parameter by giving token estimates (e.g., ~500 tokens for minimal). This goes beyond the schema's enum values, helping the agent choose based on budget.

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's verb ('Get') and resource ('condensed context snapshot for the current project'). It specifies the exact use case: call at the start of every session to load project memory. This distinguishes it from siblings that deal with individual memories or sessions, as it is positioned as a summary view.

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?

Explicitly tells when to use the tool: 'at the START of every session' and 'before doing any work.' It labels itself as 'your primary memory tool,' providing strong usage guidance. Though it doesn't list alternatives, the context makes it clear this is for loading context, not for searching or storing individual memories.

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/kunwar-shah/claudex'

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