Skip to main content
Glama

Latitude MCP Server

AI-powered prompt management for Latitude.so via Model Context Protocol

Manage PromptL prompts directly from Claude, Windsurf, or any MCP client. Features intelligent validation, dynamic tool descriptions, and git-style versioning.

npm version License: ISC


✨ Key Features

  • 🤖 Smart Validation - Client-side PromptL validation with AST-powered error messages

  • 📋 Dynamic Descriptions - Tools show available prompts and their parameters automatically

  • 🔄 Full Sync - Push/pull with automatic conflict resolution

  • 🎯 Atomic Operations - Validate ALL before pushing ANY (all-or-nothing)

  • 📚 52 Doc Topics - Comprehensive PromptL syntax guide with semantic search

  • 🏷️ Git-Style Versioning - Name your changes like commits (feat/add-auth, fix/typo)

  • ⚡ Zero Config - Just set LATITUDE_API_KEY and go


Quick Start

Installation

npm install -g latitude-mcp-server

Configuration

Set environment variables:

export LATITUDE_API_KEY="your-api-key"
export LATITUDE_PROJECT_ID="your-project-id"

Get your API key from Latitude Settings.

Usage with MCP Client

Add to your MCP client config (e.g., Claude Desktop):

{
  "mcpServers": {
    "latitude": {
      "command": "npx",
      "args": ["latitude-mcp-server"],
      "env": {
        "LATITUDE_API_KEY": "your-api-key",
        "LATITUDE_PROJECT_ID": "your-project-id"
      }
    }
  }
}

🛠️ Available Tools (7)

Tool

Type

Description

list_prompts

Read

List all prompts in LIVE

get_prompt

Read

Get full prompt content by name

run_prompt

Execute

🎯 Dynamic: Shows all prompts with their parameters

push_prompts

Write

🔄 FULL SYNC: Replace ALL prompts (deletes extras)

pull_prompts

Read

🔄 FULL SYNC: Download all prompts (deletes local first)

add_prompt

Write

🎯 Dynamic: Add/update prompts (shows available prompts)

docs

Read

Get documentation (52 topics, semantic search)

🎯 What Makes This Special?

Dynamic Tool Descriptions - The MCP server updates tool descriptions in real-time:

  • run_prompt shows: "my-prompt" (params: name, email, company)

  • add_prompt shows: "Available prompts (10): prompt-a, prompt-b, ..."

Your AI assistant sees exactly what prompts exist and what parameters they need!


🚀 Real-World Workflows

Workflow 1: New Project Setup

# Pull all prompts from LIVE to start local development
pull_prompts({ outputDir: "./prompts" })
# Downloads 10 files to ./prompts/
# Deletes any existing local .promptl files first (FULL SYNC)

What you see:

✅ Prompts Pulled from LIVE

Directory: /Users/you/project/prompts
Deleted: 0 existing files
Written: 10 files

Files:
- cover-letter-generate.promptl
- sentiment-analyzer.promptl
...

Tip: Edit files locally, then use `add_prompt` to push changes.

Workflow 2: Add New Prompt (with Dynamic Guidance)

# The tool description shows you what prompts already exist!
add_prompt({
  prompts: [{
    name: "email-writer",
    content: `---
provider: openai
model: gpt-4o
---
<user>
Write email to {{ recipient }} about {{ topic }}
</user>`
  }],
  versionName: "feat/add-email-writer"  # Optional git-style naming
})

Dynamic Description Shows:

Add or update prompt(s) in LIVE without deleting others.

Available prompts (10): cover-letter-generate, sentiment-analyzer, ...

Result:

✅ Prompts Added to LIVE

Summary:
- Added: 1
- Updated: 0

Added:
- email-writer

Current LIVE prompts (11): cover-letter-generate, ..., email-writer

Workflow 3: Run Prompt (with Parameter Discovery)

# The tool description shows you what parameters each prompt needs!
run_prompt({
  name: "email-writer",
  parameters: {
    recipient: "Alice",
    topic: "project update"
  }
})

Dynamic Description Shows:

Execute a prompt with parameters.

Available prompts (11):
- cover-letter-generate (params: job_details, career_patterns, company_name)
- email-writer (params: recipient, topic)
- sentiment-analyzer (no params)
...

Result:

✅ Prompt Executed

Prompt: email-writer

Parameters:
{
  "recipient": "Alice",
  "topic": "project update"
}

Response:
Subject: Project Update

Dear Alice,

I wanted to share an update on our project...

Tokens: 245 total

Workflow 4: Validation Catches Errors

# Try to add a prompt with nested tags (invalid PromptL)
add_prompt({
  prompts: [{
    name: "broken",
    content: `---
model: gpt-4
---
<user><assistant>Nested!</assistant></user>`
  }]
})

Validation Error (Before ANY API Call):

❌ Validation Failed - No Changes Made

1 prompt(s) have errors. Fix all errors before pushing.

### broken
Error Code: `message-tag-inside-message`
Error: Message tags cannot be inside of another message
Root Cause: Message/role tags (<system>, <user>, <assistant>, <tool>) cannot be nested.
Location: Line 4, Column 7
Code Context:

2: model: gpt-4 3: --- 4: Nested!

      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fix: Move the nested tag outside its parent. Use code block (```yaml) for examples.

Action Required: Fix the errors above, then retry.

Workflow 5: Full Sync (Initialization)

# Push local prompts to LIVE - deletes remote prompts not in your folder
push_prompts({
  folderPath: "/absolute/path/to/prompts",
  versionName: "feat/initial-prompts"  # Optional
})

Result:

✅ Prompts Pushed to LIVE

Summary:
- Added: 3
- Modified: 0
- Deleted: 8  # Removed old prompts not in your list

Current LIVE prompts (3): prompt-a, prompt-b, prompt-c

📚 Documentation Topics (52)

Core Syntax (12)

overview, structure, variables, conditionals, loops, references, tools, chains, agents, techniques, agent-patterns, mocking

Configuration (8)

config-basics, config-generation, config-json-output, config-advanced, providers-openai, providers-anthropic, providers-google, providers-azure

Messages (2)

messages-roles, messages-multimodal

Tools (4)

tools-builtin, tools-custom, tools-schema, tools-orchestration

Techniques (12)

technique-role, technique-few-shot, technique-cot, technique-tot, technique-react, technique-self-consistency, technique-constitutional, technique-socratic, technique-meta, technique-iterative, technique-step-back, technique-rag

Recipes (8)

recipe-classification, recipe-extraction, recipe-generation, recipe-chatbot, recipe-rag, recipe-analysis, recipe-moderation, recipe-support

Guides (6)

conversation-history, guide-debugging, guide-safety, guide-performance, guide-testing, guide-versioning


🛠️ Development

Build

npm run build  # Compiles TypeScript to dist/

Testing with MCP Inspector

# List all tools
npx @modelcontextprotocol/inspector \
  -e LATITUDE_API_KEY=your-key \
  -e LATITUDE_PROJECT_ID=your-id \
  --cli node dist/index.js \
  --method tools/list

# Test list_prompts
npx @modelcontextprotocol/inspector \
  -e LATITUDE_API_KEY=your-key \
  -e LATITUDE_PROJECT_ID=your-id \
  --cli node dist/index.js \
  --method tools/call \
  --tool-name list_prompts

# Test add_prompt with file
npx @modelcontextprotocol/inspector \
  -e LATITUDE_API_KEY=your-key \
  -e LATITUDE_PROJECT_ID=your-id \
  --cli node dist/index.js \
  --method tools/call \
  --tool-name add_prompt \
  --tool-arg 'filePaths=["./prompts/test.promptl"]'

# Test from npm package
npx @modelcontextprotocol/inspector \
  -e LATITUDE_API_KEY=your-key \
  -e LATITUDE_PROJECT_ID=your-id \
  --cli npx -y latitude-mcp-server@3.1.0 \
  --method tools/call \
  --tool-name list_prompts

Local Development

# Build and run
npm run build
node dist/index.js

# With environment variables
LATITUDE_API_KEY=xxx LATITUDE_PROJECT_ID=yyy node dist/index.js

# Watch mode (requires nodemon)
npm install -g nodemon
nodemon --watch src --exec "npm run build && node dist/index.js"

Project Structure

latitude-mcp-server/
├── src/
│   ├── docs/              # Documentation system (52 topics)
│   │   ├── types.ts       # Type definitions
│   │   ├── metadata.ts    # Search metadata
│   │   ├── help.ts        # Help content
│   │   ├── core-syntax.ts # Core PromptL syntax (12 topics)
│   │   ├── phase1.ts      # Tier 1 topics (8)
│   │   ├── phase2.ts      # Tier 2 topics (13)
│   │   ├── phase3.ts      # Tier 3 topics (6)
│   │   ├── techniques.ts  # Prompting techniques (8)
│   │   ├── recipes.ts     # Use case recipes (5)
│   │   └── index.ts       # DOCS_MAP + functions
│   ├── utils/             # Utilities
│   │   ├── config.util.ts # Environment config
│   │   └── logger.util.ts # Logging
│   ├── api.ts             # Latitude API client
│   ├── docs.ts            # Documentation exports
│   ├── index.ts           # MCP server entry
│   ├── server.ts          # MCP server setup
│   ├── tools.ts           # 8 MCP tools
│   └── types.ts           # Type definitions
├── scripts/
│   └── ensure-executable.js
├── .gitignore
├── package.json
├── tsconfig.json
└── README.md

Environment Variables

Variable

Required

Description

LATITUDE_API_KEY

Yes

Your Latitude API key

LATITUDE_PROJECT_ID

Yes

Your project ID

DEBUG

No

Enable debug logging


PromptL Syntax Overview

PromptL is a templating language for AI prompts:

---
provider: OpenAI
model: gpt-4o
temperature: 0.7
schema:
  type: object
  properties:
    answer:
      type: string
  required: [answer]
---
<system>
You are a helpful assistant.
</system>

<user>
{{ question }}
</user>

Key Features:

  • YAML config header (provider, model, temperature)

  • Message tags (<system>, <user>, <assistant>)

  • Variables ({{ name }})

  • Conditionals ({{ if }}, {{ else }})

  • Loops ({{ for item in items }})

  • Tools (function calling)

  • Chains (multi-step <step>)

  • Agents (autonomous type: agent)

Use docs({ action: "get", topic: "overview" }) for complete guide.


📖 Tool Reference

list_prompts()

List all prompts in LIVE version.

Returns: Array of prompt names with project ID

Example:

list_prompts()
// Returns: cover-letter-generate, sentiment-analyzer, email-writer (10 total)

get_prompt({ name })

Get full prompt content by name.

Parameters:

  • name (string) - Prompt name

Returns: Full PromptL content with config and messages

Example:

get_prompt({ name: "email-writer" })
// Returns full .promptl content

run_prompt({ name, parameters })

🎯 Dynamic: Execute a prompt. Tool description shows all prompts with their parameters!

Parameters:

  • name (string) - Prompt name

  • parameters (object, optional) - Input parameters

Returns: AI response with token usage

Dynamic Description:

Available prompts (10):
- email-writer (params: recipient, topic)
- sentiment-analyzer (no params)
- cover-letter-generate (params: job_details, career_patterns, company_name)

Example:

run_prompt({
  name: "email-writer",
  parameters: { recipient: "Alice", topic: "update" }
})

add_prompt({ prompts?, filePaths?, versionName? })

🎯 Dynamic: Add or update prompts without deleting others. Tool description shows available prompts!

Behavior: If prompt exists → overwrites. If new → adds. Never deletes other prompts.

Parameters (choose one):

Option A - Direct content:

  • prompts (array) - Array of { name, content }

Option B - From files:

  • filePaths (array) - Array of paths to .promptl files

Common:

  • versionName (string, optional) - Git-style name like feat/add-auth or fix/typo

Returns: Summary of added/updated prompts

Example:

add_prompt({
  filePaths: ["./prompts/new-prompt.promptl"],
  versionName: "feat/add-new-prompt"
})

push_prompts({ prompts?, filePaths?, versionName? })

🔄 FULL SYNC: Replace ALL prompts in LIVE. Deletes remote prompts not in your list.

Use for: Initial setup, complete sync, resetting LIVE to match local.

Parameters (choose one):

Option A - Direct content:

  • prompts (array) - Array of { name, content }

Option B - From files:

  • filePaths (array) - Array of paths to .promptl files

Common:

  • versionName (string, optional) - Git-style name like feat/initial-setup

Returns: Summary of added/modified/deleted prompts

Example:

push_prompts({
  filePaths: ["./prompts/prompt-a.promptl", "./prompts/prompt-b.promptl"],
  versionName: "feat/complete-rewrite"
})

pull_prompts({ outputDir? })

🔄 FULL SYNC: Download all prompts from LIVE. Deletes existing local .promptl files first.

Use for: Initial clone, resetting local to match LIVE.

Parameters:

  • outputDir (string, optional) - Output directory (default: ./prompts)

Returns: List of downloaded files

Example:

pull_prompts({ outputDir: "./my-prompts" })

docs({ action, topic?, query? })

Access comprehensive PromptL documentation (52 topics).

Parameters:

  • action (string) - "help" (overview), "get" (topic), or "find" (search)

  • topic (string, optional) - Topic name for "get"

  • query (string, optional) - Search query for "find"

Returns: Documentation content

Examples:

docs({ action: "help" })                      // Overview
docs({ action: "find", query: "json output" }) // Semantic search
docs({ action: "get", topic: "chains" })       // Specific topic

✅ Validation Features

Client-Side Validation with AST

All write operations (add_prompt, push_prompts) validate prompts before making API calls using the official promptl-ai library.

Benefits:

  • Fast feedback - No wasted API calls

  • 🎯 Precise errors - Exact line and column numbers

  • 📝 Code frames - See surrounding context with ^~~~ pointer

  • 🤖 LLM-actionable - Errors include root cause and fix suggestions

Atomic Operations

Validate ALL, push ALL or NOTHING:

// Trying to push 10 prompts, but 1 has an error
add_prompt({
  filePaths: [
    "./prompts/valid-1.promptl",
    "./prompts/valid-2.promptl",
    "./prompts/BROKEN.promptl",  // Has nested tags
    // ... 7 more valid prompts
  ]
})

// Result: NOTHING is pushed
// Error shows exactly what's wrong in BROKEN.promptl
// Fix the error, retry → all 10 push successfully

Error Message Example

❌ Validation Failed - No Changes Made

1 prompt(s) have errors.

### my-prompt
Error Code: `message-tag-inside-message`
Error: Message tags cannot be inside of another message
Root Cause: Message/role tags cannot be nested inside each other.
Location: Line 107, Column 1
Code Context:

105: ## EXAMPLES 106: 107:

  ^~~~~~~~~~~~

108: questions: 109: - id: q1

Fix: Move the nested tag outside its parent. Use code block (```yaml) instead.

Supported Error Types

  • message-tag-inside-message - Nested role tags

  • content-tag-inside-content - Nested content tags

  • config-not-found - Missing YAML frontmatter

  • invalid-config - Malformed YAML

  • unclosed-block - Missing closing tag

  • variable-not-defined - Undefined variable

  • invalid-tool-call-placement - Tool call outside <assistant>

  • ...and more from official promptl-ai compiler


🔄 Migration Guide (v2 → v3)

Tool Changes

Old Tool (v2)

New Tool (v3)

Notes

append_prompts

add_prompt

Always overwrites if exists (no overwrite param needed)

replace_prompt

add_prompt

Same behavior, unified tool

Migration:

// OLD (v2)
append_prompts({ filePaths: [...], overwrite: true })
replace_prompt({ filePath: "./prompt.promptl" })

// NEW (v3)
add_prompt({ filePaths: [...] })  // Always overwrites if exists

🔧 Troubleshooting

"Validation Failed" Errors

Problem: Prompt fails with nested tag error

Solution: The error shows exact location with code frame:

Error Code: `message-tag-inside-message`
Location: Line 4, Column 7
Code Context:
4: <user><assistant>Nested!</assistant></user>
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Fix: Move the nested tag outside its parent.

Follow the fix suggestion - errors are LLM-actionable!

"No Changes Made" After Push

Problem: push_prompts reports no changes

Cause: All prompts are already up to date (content matches LIVE)

Solution: This is normal - no action needed

Version Naming Best Practices

Good:

  • feat/add-sentiment-analyzer

  • fix/typo-in-greeting

  • refactor/simplify-prompts

  • docs/update-examples

Avoid:

  • test (too vague)

  • update (what was updated?)

  • v1.2.3 (use semantic versioning elsewhere)

Dynamic Descriptions Not Updating

Problem: Tool descriptions show old prompt list

Cause: Cache not refreshed (30s TTL)

Solution: Wait 30 seconds or restart MCP server


Contributing

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Run npm run build to verify

  5. Submit a pull request


License

ISC License - see LICENSE file for details



Support


Built with ❤️ for the MCP ecosystem

-
security - not tested
F
license - not found
-
quality - not tested

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/yigitkonur/latitude-mcp-server'

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