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.
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.
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 requiredmessageand an optionalprefixand returns it. No
dependencies, nothing to guard. Start here. - ContentLookupTool (
content_lookup) takes anode_idand returns the title and body.checkAccess()requires theaccess contentpermission, and the run also checks per-nodeviewaccess, 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 exampledrupal://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/jsonapiwould
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 and it resolves to node
world (1)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, for every node type. Requires
description}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 alabelkey (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+resourcemessage, showing how to embed a resource reference in a prompt. - comprehensive_lesson: all four message types at once,
text,image,audio, andresource. - 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_lookupcheck theaccess contentpermission;content_lookupthen
checks per-nodeviewaccess 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 serverpermission.
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 andexecute()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 templatedmessages.
Related projects
- MCP Server: the runtime and plugin contracts this module demonstrates.
- MCP Server UI: admin forms to enable, configure, and inspect plugins.
- MCP Server Views: build MCP resources from Views, no code required.
- MCP Server Tool Bridge: generate MCP tools from existing Drupal plugin definitions.
- MCP Server OAuth: OAuth 2.1 authentication for the MCP endpoint.