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