Drupal is a registered trademark of Dries Buytaert
Release: Leaflet 10.4.11 Minor update available for module leaflet (10.4.11). Release: Session Inspector 1.0.8 Minor update available for module session_inspector (1.0.8). Release: Migrate QA 2.0.4 Minor update available for module migrate_qa (2.0.4). Release: CKEditor Description List 3.0.0 Major update available for module ckeditor_descriptionlist (3.0.0). Release: FlowDrop 2.4.0 Minor update available for module flowdrop (2.4.0). Release: JWT Token Refresh 1.0.4 Minor update available for module jwt_token_refresh (1.0.4). Release: ConReg 1.0.0-beta1 First beta version released for module conreg (1.0.0-beta1). Release: AI Image Studio 1.0.0-beta8 New beta version released for module ai_image_studio (1.0.0-beta8). Usage Milestone: Statistics Counter Module statistics_counter crossed 1,000 active installs. Module Revived: Decoupled Router 2.0.7 Module decoupled_router updated after 11 months of inactivity (2.0.7).

This module provides example code and configurations generated with AI assistance to help users learn and build with Drupal. It showcases various Drupal functionalities and coding patterns, making it useful for developers looking to understand how to implement specific features or explore AI-assisted Drupal development.

Built by a human using an AI assistant: 🤖 ➕ 🧠

A lot of the code in this module has been created using an AI assistant using Strikethroo & Kenkeep for improved Drupal code generation.

Overview

Working reference implementations for every plugin type in the MCP Server plugin system. Enable the module and a fresh site has tools an AI can call, resources it can
read, and prompts it can run, all before you write a single line of plugin code.

The code is meant to be read as much as run. Each example is one small, final class that demonstrates exactly one decision the MCP Server contracts ask you to make.

Requires MCP Server 2.x. MCP Server UI is strongly recommended: it gives you the admin forms to enable,
configure, and inspect everything shipped here.

What ships

2 tools

echo is a one-minute "is MCP wired up?" smoke test. content_lookup loads a node by ID and shows how an access check works.

1 resource template

content_entity exposes every content entity on the site at drupal://entity/{entity_type}/{entity_id}, serialized through JSON:API.

1 resource provider

content_type_list serves one fixed URI, drupal://content-types, so a model can discover what the site publishes before asking for
it.

2 completion providers + 8 prompts

Autocomplete backed by an entity query or a static list, plus eight prompt configurations covering text, image, audio, and resource messages.

Install and go

composer require drupal/mcp_server_examples
drush en mcp_server_examples -y
drush cr

That is the whole setup. On install, config/install/ enables the content_entity resource template and the content_type_list resource, installs all eight prompt configurations, and both tools default to enabled. Point an MCP client at your endpoint and tools/list, resources/list, resources/templates/list, and prompts/list all
come back populated.

Read this before installing on anything public.

Both tools ship enabled. content_lookup respects node access,
and echo is harmless, but a production site should not expose example plugins by default. Disable them in MCP Server UI, or keep this module to development environments. It
is a teaching aid, not a feature.

Tools

Both are final classes extending ToolPluginBase, described entirely by the #[Tool] attribute: input
schema, output schema, and the readOnly / destructive / idempotent / openWorld hints the SDK advertises to clients.

  • EchoTool (echo) takes a required message and an optional prefix and returns it. No
    dependencies, nothing to guard. Start here.
  • ContentLookupTool (content_lookup) takes a node_id and returns the title and body. checkAccess() requires the access content permission, and the run also checks per-node view access, returning a
    denial rather than leaking whether the node exists.

Resources

content_entity (template)

One template covers every content entity type on the site.

  • URI pattern: drupal://entity/{entity_type}/{entity_id}. For example drupal://entity/node/1, drupal://entity/user/42, drupal://entity/taxonomy_term/7.
  • Output: application/vnd.api+json, produced by Drupal's own JSON:API serializer. It is the same document /jsonapi would
    give you, with field formatters, relationships, links, and cache metadata intact.
  • Access: delegated to $entity->access('view', $account). Autocomplete suggestions are filtered by the same check, so a model never sees the label of
    an entity it cannot read.
  • Configurable: Require entities to have canonical URL (on by default) and a Denied entity types checklist to exclude types you would rather not expose.

Entity ID autocompletion returns Label (ID), and the provider normalizes that back to a bare ID before loading, so a client can send Hello
world (1)
and it resolves to node 1 transparently.

content_type_list (provider)

About the simplest useful resource you can write: a single URI, drupal://content-types, returning a sorted JSON array of {id, label,
description}
for every node type. Requires access content, cached against the node_type_list cache tag and the user.permissions context.

Copy this one when you want to hand a model a small, cheap discovery document.

Argument completion

Completion providers turn a free-text argument into a picklist. Both return plain string arrays, which MCP Server hands to completion/complete.

  • EntityQueryCompletionProvider (entity_query) queries entities through Drupal's entity reference selection handler, so types without a label key (User, notably) still get their native lookup. Configure the entity type, an optional bundle filter, and a result format of canonical URL or
    Label (ID). Capped at 10 suggestions and filtered by view access.
  • StaticListCompletionProvider (static_list) is a hardcoded list of values, one per line. Zero queries, predictable, and the right pick for a fixed
    vocabulary.

They chain. The multi_provider_prompt configuration attaches both to the same argument, which is the pattern to copy when you want a curated shortlist plus live
content.

Prompt configurations

Eight prompts ship as pure YAML (no PHP) and appear in prompts/list straight after install. Between them they cover every message type and argument shape a prompt is
likely to need.

  • tennis_tip_basic and test_with_args: the minimal cases, with required and optional arguments both completed from static lists.
  • serve_technique and audio_coaching: single-medium messages (image, audio) with minimal
    arguments.
  • tennis_resources: a text + resource message, showing how to embed a resource reference in a prompt.
  • comprehensive_lesson: all four message types at once, text, image, audio, and resource.
  • match_strategy: three arguments, mixing required and optional with per-argument completion.
  • multi_provider_prompt: one argument, two chained completion providers.

Access control

Nothing here invents its own permission model. Every example defers to a check Drupal already performs:

  • The resource template calls $entity->access('view', $account).
  • The resource provider and content_lookup check the access content permission; content_lookup then
    checks per-node view access as well.
  • Enumeration and reads use the same check, so listings never advertise something a read would deny.
  • Reaching the MCP endpoint at all requires MCP Server's access mcp server permission.

Where to look in the code

If you are here to write your own plugin, these are the seams each contract gives you:

  • Tool: the #[Tool] attribute declares the schemas and behavior hints; checkAccess() is the access seam and execute() is the side-effect seam.
  • ResourceTemplateProvider: getUriTemplate() declares the URI shape, getResources() enumerates, getCompletionProviders() wires autocomplete to a URI variable, getResourceContent() resolves a concrete URI, checkAccess() guards both.
  • ResourceProvider: the same shape with a fixed URI instead of a template. The shortest path to a working resource.
  • ArgumentCompletionProvider: getCompletions() is the only method the SDK calls; the configuration form is yours to define.
  • Prompt configuration: YAML only, with arguments, completion_providers, and templated messages.

Related projects

Activity

Tracked releases
2
Tracked since
Jul 2026
Latest release
3 weeks ago
Releases (12 mo)
2 ▲ from 0
Maintenance
Active

Releases

Version Type Release date
1.0.0-beta1 Pre-release Jul 29, 2026
1.x-dev Dev Jul 2, 2026