Skip to content

MCP Server

The Model Context Protocol (MCP) server bridges AI assistants like Claude to your indexed codebase, enabling intelligent code search, pattern recognition, and Wikit-aware development.

Overview

The MCP server provides:

  • Semantic Code Search: Find code by meaning across all projects
  • WordPress Pattern Recognition: Understand hook registrations, invocations, and ACF fields
  • Wikit Block Discovery: Search indexed Gutenberg blocks and their schemas
  • Project Management: Inspect project indexing status and manifests
  • Saved Searches & History: Persist common queries and review past searches

Architecture

%%{init: {'theme':'neutral'}}%%
graph TB
    subgraph "AI Layer"
        Claude[Claude Desktop]
        Cursor[Cursor IDE]
        Other[Other AI Tools]
    end

    subgraph "MCP Server"
        Server[MCP Server<br/>Host port 6765<br/>SSE transport]
        Tools[MCP Tools]
    end

    subgraph "Data Layer"
        Qdrant[(Vector DB<br/>Qdrant)]
        Git[Git Repositories]
        Projects[WordPress Projects]
    end

    Claude -->|MCP Protocol| Server
    Cursor -->|MCP Protocol| Server
    Other -->|MCP Protocol| Server

    Server --> Tools
    Tools --> Qdrant
    Tools --> Git
    Tools --> Projects

Available Tools

Code Search Tools

search_codebase

Semantic search across all indexed repositories.

python
# Example usage in Claude
search_codebase(
    query="custom post type registration",
    project="my-site",
    file_types="php",
    exclude_patterns=["tests/"],
    include_context=True,
    limit=10
)

Parameters:

  • query (string): Search query
  • project (string, optional): Project name to scope search ("platform", "framework", or a project name). When omitted, searches all collections.
  • file_types (string | list, optional): Filter by file extensions. Accepts a comma-separated string (e.g. "php,js") or a list.
  • component_type (string, optional): Filter by function, class, hook, etc.
  • exclude_patterns (list, optional): Path substrings to exclude (e.g. ["vendor/", "tests/"])
  • include_context (bool, optional): Include surrounding code lines (default: false)
  • context_lines (int, optional): Number of context lines before/after, 1-20 (default: 5)
  • limit (int): Maximum results (default: 20)

Returns: A result object with status, count, collection, query, search_type, execution_time_ms, and a results array. Each result entry:

json
{
  "file_path": "wp-content/themes/custom/functions.php",
  "component_type": "function",
  "name": "register_portfolio_post_type",
  "line_number": 45,
  "language": "php",
  "content": "function register_portfolio_post_type() {...}",
  "repository": "my-site",
  "score": 0.89
}

search_all_collections

Search across ALL indexed collections at once (framework, platform, and every project). Useful when you don't know which collection holds the code.

python
search_all_collections(
    query="user authentication flow",
    file_types="php"
)

search_functions

Find specific functions by name. Supports fuzzy matching across naming conventions (e.g. getUser finds get_user).

python
search_functions(
    function_name="get_user",
    project="my-site",
    language="php",
    fuzzy_match=True
)

search_classes

Find class definitions by name. Supports fuzzy matching across naming conventions.

python
search_classes(
    class_name="CustomPostType",
    project="my-site",
    fuzzy_match=True
)

Parameters: class_name, project (optional), fuzzy_match (default true). There is no language parameter.

search_code_patterns

Find code by structural relationship: class inheritance, interface implementation, or function calls.

python
search_code_patterns(
    pattern_type="extends",   # "extends" | "implements" | "calls_function"
    pattern_value="WP_Widget"
)

search_function_callers

Find what functions call a given function (reads the indexed calls metadata).

python
search_function_callers(function_name="render_block", project="my-site")

search_function_calls

Find what functions a given function calls.

python
search_function_calls(function_name="render_block", project="my-site")

WordPress-Specific Tools

search_wordpress_hooks

Find WordPress hook registrationsadd_action() and add_filter() calls.

python
search_wordpress_hooks(
    hook_name="init",
    hook_type="action",   # "action" | "filter"
    project="my-site"
)

To find where hooks are invoked (do_action() / apply_filters()), use search_hook_invocations instead.

search_hook_invocations

Find where WordPress hooks are invoked — do_action() and apply_filters() calls (not registrations).

python
search_hook_invocations(
    hook_name="save_post",
    invocation_type="action",   # "action" for do_action, "filter" for apply_filters
    project="my-site"
)

search_acf_fields

Find Advanced Custom Fields (ACF) field and field-group definitions.

python
search_acf_fields(
    field_name="hero_image",
    field_type="image",
    project="my-site"
)

search_acf_field_usages

Find where ACF fields are used in code — get_field(), get_sub_field(), the_field(), have_rows() calls.

python
search_acf_field_usages(
    field_name="hero_image",
    project="my-site"
)

wordpress_docs

Search WordPress core documentation.

python
wordpress_docs(
    query="register_post_type",
    type="function",
    get_details=True
)

Wikit-Specific Tools

search_wikit_blocks

Search indexed Wikit Gutenberg blocks. Can filter by attribute, supported feature, or category.

python
search_wikit_blocks(
    block_name="hero",        # Optional
    has_attribute="backgroundType",  # Optional
    supports="align",         # Optional
    category="wdg-layout"     # Optional
)

Returns: A result object with status, query, filters, count, and a results array of indexed block components.

get_block_schema

Get the full parsed block.json schema for a Wikit block — attributes, supports, parent, keywords, and category.

python
get_block_schema(
    block_name="wdg/hero",   # full or partial block name
    project="my-site"        # Optional
)

Project Management Tools

get_project_info

Get project details and indexing status.

python
get_project_info(
    project_name="my-site"
)

Returns: Indexing status from the vector database, plus the project manifest (project.yml) when present.

json
{
  "status": "success",
  "project": "my-site",
  "indexed": true,
  "collection": "project_my_site",
  "vectors_count": 1450,
  "languages": ["php", "javascript"],
  "component_types": ["function", "class", "wikit_block"],
  "repositories": ["my-site"],
  "manifest": { "jira_project_key": "MS", "hosting": "pantheon" }
}

list_collections

List all vector database collections.

python
list_collections()

Recent Changes Tools

search_recent_changes

Get recently indexed code.

python
search_recent_changes(
    project="my-site",
    limit=10
)

batch_search

Run multiple searches in a single call (parallel by default). Each entry is a search config dict.

python
batch_search(
    searches=[
        {"id": "hooks", "query": "add_action", "file_types": "php"},
        {"id": "blocks", "query": "registerBlockType", "file_types": "js"}
    ],
    parallel=True,
    fail_fast=False
)

Parameters: searches (list of dicts — each may include id, query, project, file_types, component_type, exclude_patterns, limit), parallel (default true), fail_fast (default false). Maximum 10 concurrent searches per call.

Saved Searches & History

Save a search with a label for quick recall.

python
save_search(label="auth-hooks", query="authenticate", filters={"file_types": "php"})

Retrieve a saved search by label; optionally execute it.

python
get_saved_search(label="auth-hooks", execute=True)

list_saved_searches

List saved searches, sorted by last_used, use_count, label, or created_at.

python
list_saved_searches(sort_by="last_used", limit=20)

Delete a saved search by label.

python
delete_saved_search(label="auth-hooks")

get_search_history

Retrieve recent search history with result counts and timing.

python
get_search_history(limit=50, query_filter="payment")

MCP Auth Proxy (OAuth-Protected Services)

A separate MCP Auth Proxy (mcp-server/proxy_server.py) runs on host port 6766 and brokers authenticated access to external MCP services. Proxied tools appear at runtime with the mcp__mcp-auth-proxy__<service>_* prefix.

  • SSE endpoint: http://localhost:6766/sse
  • Health check: curl http://localhost:6766/health

Configured services (in mcp-server/registry.json):

ServiceTool prefixTransportDescription
Atlassianatlassian_*HTTP (proxied)Jira issues, Confluence pages, search
Figmafigma_*HTTP (proxied)Design files, variables, screenshots
Read.airead-ai_*HTTP (proxied)Meeting transcripts and summaries
Slackslack_*HTTP (proxied)Messages, channels, search, canvases

Authenticate a service with wdg mcp auth <service> (opens a browser for OAuth) and check token validity with wdg mcp status.

Configuration

Server Configuration

The MCP server is reachable from the host at port 6765 (set by MCP_SERVER_PORT in .env.defaults) over the SSE transport. The host port maps to the container's internal port 87658765 is container-internal only and should never appear in host or client configuration.

yaml
# docker-compose.yml (illustrative)
mcp-server:
  ports:
    - "6765:8765"   # host 6765 -> container 8765
  environment:
    - MCP_PORT=8765         # container-internal listen port
    - QDRANT_HOST=qdrant
    - QDRANT_PORT=6333

Host-facing endpoint: http://localhost:6765/sse

Client Configuration

Claude Desktop

Add to Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

json
{
  "mcpServers": {
    "wdg-local": {
      "url": "http://localhost:6765/sse"
    }
  }
}

Cursor IDE

Add to Cursor settings (.cursor/mcp.json):

json
{
  "servers": {
    "wdg-local": {
      "url": "http://localhost:6765/sse",
      "name": "WDG AI Dev Environment"
    }
  }
}

Usage Examples

Example 1: Finding Similar Code

User prompt to AI:

"Find all places where we validate email addresses"

AI uses MCP:

python
search_codebase(
    query="email validation",
    file_types=["php", "js"]
)

Result:

  • is_valid_email() in utils.php
  • validateEmailAddress() in forms.js
  • Email regex patterns
  • WordPress sanitize_email() usage

Example 2: Understanding Wikit Blocks

User prompt:

"What Wikit blocks are available for layout?"

AI uses MCP:

python
search_wikit_blocks()

AI responds with:

  • Hero block
  • Grid block
  • Columns block
  • Section block
  • Container block

User prompt:

"In the client-website project, show me how we handle custom post types"

AI uses MCP:

python
search_codebase(
    query="custom post type registration",
    project="client-website",
    component_type="function"
)

Advanced Features

Contextual Awareness

The MCP server understands:

  • Project context: Scopes searches to active project
  • File relationships: Knows which files depend on others
  • Git history: Can reference specific commits
  • WordPress conventions: Understands hooks, filters, CPTs

Search across multiple projects:

python
# Search in all projects
search_codebase(
    query="user authentication",
    project=None  # Searches all projects
)

# Compare implementations across projects
search_functions(
    function_name="get_user",
    project=None
)

Pattern Recognition

The MCP server identifies:

  • Wikit patterns: Recognizes WDG's standard implementations
  • WordPress best practices: Identifies secure coding patterns
  • Code reusability: Finds similar implementations

Performance

Response Times

OperationAverage TimeNotes
search_codebase50-100msDepends on collection size
search_functions20-50msDirect lookup
search_wikit_blocks10-20msSmall collection
get_project_info<10msCached
list_collections<10msDirect query

Caching

The MCP server implements caching for:

  • Project metadata (15 min TTL)
  • Collection lists (5 min TTL)
  • Wikit block catalog (1 hour TTL)

Optimization

python
# Limit result count for faster responses
search_codebase(query="...", limit=5)

# Use filters to reduce search space
search_codebase(
    query="...",
    project="my-site",
    file_types=["php"],
    component_type="function"
)

Security

Network Security

yaml
# Bind only to localhost (default)
MCP_HOST=127.0.0.1  # Local only

# Or bind to all interfaces (be careful!)
MCP_HOST=0.0.0.0  # Accessible on network

Authentication

Currently, the MCP server runs locally without authentication. For production deployments:

python
# Add API key authentication
MCP_API_KEY=your-secret-key

# Or use JWT tokens
MCP_JWT_SECRET=your-jwt-secret

Data Privacy

  • All queries stay local
  • No external API calls for search
  • Code never leaves your machine
  • Project data is isolated

Troubleshooting

Server Not Responding

bash
# Check if server is running (host-facing SSE server health endpoint)
curl http://localhost:6765/health

# View server logs
docker logs wdg-mcp-server

# Restart server
docker compose restart mcp-server

Connection Refused

bash
# Check port is not in use
lsof -i :6765

# Verify Docker network
docker network inspect wdg-network

# Check firewall settings
sudo ufw status

Slow Responses

bash
# Check Qdrant status
curl http://localhost:6333/collections

# Monitor server resources
docker stats wdg-mcp-server

# Reduce search limits
# Use more specific queries

Empty Results

bash
# Verify collections exist
curl http://localhost:6333/collections

# Check indexing status
wdg status

# Re-index if needed
wdg index my-site

API Reference

Health Check

The SSE app exposes a single plain-HTTP health endpoint. There are no REST /tools, /metrics, or WebSocket endpoints — tool discovery and invocation happen over the MCP/SSE protocol, not REST.

bash
curl http://localhost:6765/health

Response:

json
{
  "status": "ok",
  "version": "0.1.0"
}

The version field is read from the platform VERSION file (falls back to "unknown" if unreadable). For the full tool list and parameters, see the MCP API Reference.

Best Practices

1. Use Specific Queries

python
# Good: Specific and contextual
search_codebase(
    query="WordPress REST API endpoint registration",
    component_type="function"
)

# Bad: Too vague
search_codebase(query="api")

2. Scope to Project

python
# Good: Scoped search
search_codebase(
    query="user authentication",
    project="client-website"
)

# Bad: Unscoped (slower)
search_codebase(query="user authentication")

3. Use Appropriate Tools

python
# Good: Use specific tool
search_functions(function_name="get_user")

# Bad: Generic search
search_codebase(query="get_user")

4. Limit Results

python
# Good: Limited results
search_codebase(query="...", limit=5)

# Bad: Too many results
search_codebase(query="...", limit=100)

Integration Examples

Claude Desktop

Create a custom slash command:

json
// ~/Library/Application Support/Claude/commands/search-code.json
{
  "command": "/search-code",
  "description": "Search WDG codebase",
  "handler": {
    "tool": "search_codebase",
    "parameters": {
      "query": "$input"
    }
  }
}

Cursor IDE

Use in Cursor's chat:

User: "@wdg Find email validation in my-site project"

Cursor: [Uses MCP search_codebase tool]
Found 3 implementations:
1. is_valid_email() in utils.php
2. validateEmail() in forms.js
3. Email validation in checkout.php

Monitoring

View Real-Time Logs

bash
# Follow MCP server logs
docker logs -f wdg-mcp-server

# Filter for searches
docker logs wdg-mcp-server | grep "search_codebase"

Search History

The server records search activity internally. Review it through the get_search_history MCP tool rather than a REST endpoint:

python
get_search_history(limit=50)

Next Steps: