Skip to content

MCP server integration

Goal: connect the MCP client of your choice to your site’s MCP server, and know what the server exposes before you point an agent at it.

  • Your MCP client connected and authorized: Claude Code, Claude Desktop, Cursor, VS Code, GitHub Copilot, OpenCode, Codex, or MCP Inspector.
  • A working map of the server: drupal:// and canvas:// resource URIs, the tool catalog by domain, and the limits to design around.
  • The MCP Server experimental feature enabled on your site; the MCP quickstart covers enabling it and the fastest path to a first tool call with Claude Code.
  • An MCP-capable client installed; the MCP quickstart sets one up (Claude Code) if you don’t have one yet.
  1. Copy the connection details from your site

    Section titled “Copy the connection details from your site”

    In the site admin UI: Configurations > Experimental features > MCP Server > Configure. The panel gives you everything a client needs:

    • MCP client configuration: the server URL and parameters to paste into claude_desktop_config.json or Cursor.
    • MCP Inspector: a ready-made command to launch the debug environment.
    • OAuth configuration: the scopes and authentication parameters the server accepts.

    You typically don’t change anything here; copy the connection URL and move on.

  2. Understand how credentials reach the server

    Section titled “Understand how credentials reach the server”

    The server speaks MCP over streamable HTTP at {DRUPAL_SITE_URL}/mcp, and every request is authorized by OAuth 2.0: a bearer token on each HTTP request. There is no API key to paste. In practice: the client registers itself with the site and opens your browser; you sign in (if you aren’t already) and click Allow; from then on the client attaches and refreshes its token without your involvement.

    How the handshake works, step by step

    An unauthenticated request gets a 401 whose WWW-Authenticate header carries only Bearer realm="mcp_server" (captured 2026-07-03): no scope list, no metadata URL. The client therefore derives the discovery locations from the server URL itself: the resource metadata at {DRUPAL_SITE_URL}/.well-known/oauth-protected-resource/mcp lists the scopes the server accepts, and the site’s /.well-known/oauth-authorization-server document names the endpoints. From there the client registers itself at the registration_endpoint (/oauth/register, dynamic client registration; this is how Claude Code and other MCP clients self-register), then opens the browser window where you approve the connection. The resulting access token rides on every subsequent request.

    The whole exchange, from the challenge to authorized tool calls: an unauthenticated request is refused, the client discovers the OAuth metadata and self-registers, and you approve in the browser. The token then rides every request until it expires and the client silently re-authorizes.

    Three consequences worth knowing:

    • The agent acts as you. Tool calls carry your authorization, so what the agent can read and write is bounded by what you approved.
    • Tokens expire after 300 seconds. Clients re-authorize silently, but a long-idle session may drop you back into the browser flow.
    • Headless setups don’t need the browser: tokens minted by the site’s existing API clients also work, a client_credentials grant against {DRUPAL_SITE_URL}/oauth/token (no scope parameter needed). Pass the token as an Authorization header in the client’s server configuration, e.g. claude mcp add acquia-source-mcp {DRUPAL_SITE_URL}/mcp --transport http --header "Authorization: Bearer <token>"; remember it expires after 300 seconds, so CI mints one per run.

    The OAuth machinery (grants, scopes, token lifetimes) is the same one behind the Content API; the authentication guide covers it in depth.

  3. The canonical client, documented end to end in the quickstart:

    Terminal window
    claude mcp add acquia-source-mcp <URL> \
    --transport http --scope project

    Then inside Claude Code: /mcp → select acquia-source-mcpAuthenticate → complete the browser flow. --scope project writes the server to .mcp.json in the project root so the whole team shares it; use --scope user instead if the server should follow you across projects rather than live in one repo.

    MCP Inspector (for debugging, not day-to-day use):

    Terminal window
    npx @modelcontextprotocol/inspector --transport http

    The command starts a local web UI and prints its address (http://localhost:6274/…); open it in a browser. In the Inspector UI: paste the site’s MCP server URL, set connection type to Direct, click Connect, complete the OAuth flow for dynamic client registration, and click Allow. You can then browse List Resources, List Templates, and Tools: the fastest way to see exactly what your server exposes, live.

  4. Browse what the server exposes as resources

    Section titled “Browse what the server exposes as resources”

    Resources are read-only data, addressed by URI. The server uses two schemes: drupal:// for the content model and content entities, canvas:// for Drupal Canvas pages and components. Reading one takes no special syntax: in Claude Code, just ask (Read drupal://content-types and summarize the fields); in MCP Inspector, use List Resources and click through.

    Fixed resources list things:

    URI Returns
    drupal://content-types The site’s content types, each with its workflow and schema URI
    drupal://media-types Media types
    drupal://vocabularies Taxonomy vocabularies
    drupal://filter-formats Text formats
    drupal://content-types/fields Content-type field storages
    drupal://field-types Available field types, by category
    drupal://menus The site’s menus, each with an items_uri for its item tree
    drupal://site-info Site name and configured home page
    canvas://components Drupal Canvas components
    canvas://pages Drupal Canvas pages, with live and draft metadata
    canvas://page-regions Global page regions (header, footer, …) for the active theme
    canvas://auto-saves Pending auto-saved drafts, with the keys the publish/discard tools need

    Resource templates take parameters for point lookups:

    URI template Returns
    drupal://entity/{entity_type_id}/{id} One entity (e.g. drupal://entity/node/17)
    drupal://content-types/{bundle} JSON Schema for a content type (e.g. drupal://content-types/article)
    drupal://content-types/{bundle}/fields/{field_name} One field’s instance and storage configuration
    drupal://media-types/{bundle} JSON Schema for a media type
    drupal://vocabularies/{vid} JSON Schema for a vocabulary
    drupal://menus/{menu_name} One menu’s item tree, with the item IDs the menu tools take
    canvas://components/{id} One Canvas component’s detail

    So “what fields does an article have?” is one read of drupal://content-types/article, and “what components can I place on a page?” is one read of canvas://components. Response shapes for each are specified in the MCP server reference.

  5. Tools are the operations that change state (plus a few parameterized reads), 38 in all. Each name links to its entry in the reference, which specifies inputs, responses, and errors:

    Content management, read and write content entities: create_node · update_node · batch_create_nodes · list_entities

    Content modeling, define and evolve content structure: create_content_type · add_field_to_content_type · batch_add_fields_to_content_type · update_field_config

    Taxonomy and menus, vocabularies, terms, menus, and menu items: create_vocabulary · get_or_create_term · create_menu · update_menu · create_menu_item · update_menu_item · delete_menu_item

    Media, file uploads, remote video, and DAM references: create_media · create_remote_video · create_dam_media

    Site settings, site name, logo, and homepage: update_site_settings · update_site_logo · set_homepage

    Drupal Canvas pages and components, pages, component-based layouts, and the global page regions: create_canvas_page · update_canvas_page · delete_canvas_page · get_page_layout · set_page_layout · add_component_to_page · batch_add_components_to_page · move_component · remove_component · update_component_props · get_page_region_layout · set_page_region_layout · add_component_to_page_region

    Drafts and publishing, make drafts live, or throw them away: publish_canvas_page · publish_auto_saves · discard_auto_saves · get_canvas_preview_signed_url

    The combination is where the value is: an agent reads drupal://content-types/article to learn the shape, then calls batch_create_nodes to generate structured content in bulk; or reads canvas://components and composes a landing page with create_canvas_page, batch_add_components_to_page, and publish_canvas_page.

    • Experimental. The server may change or be removed without notice, and it is off by default, per-site opt-in under Configurations > Experimental features.
    • Your permissions are the ceiling. Every tool call runs with the authorization you granted in the OAuth flow; the server grants agents no access you don’t have.
    • Content types need a workflow that already exists, and no tool creates one. create_content_type requires a workflow parameter naming an existing workflow (empty or null is rejected), the catalog has no workflow-creation tool, and a fresh Source CMS site ships with none. On a fresh site the call fails with Workflow "…" does not exist. until an administrator creates a workflow in the admin UI (content workflows, on docs.acquia.com). Check what exists first: drupal://content-types shows each content type’s workflow.
    • No content deletion. The catalog has create and update tools for content entries, media, and terms, but no delete: the only delete tools are delete_menu_item and delete_canvas_page. Deleting content stays a human job in the admin UI or JSON:API.
    • Batch creation covers nodes, fields, and components (batch_create_nodes, batch_add_fields_to_content_type, batch_add_components_to_page); media and terms are created one call at a time.
    • Many writes land as drafts, not live changes. Canvas layout edits, menu items, and staged site settings go to the auto-save channel; nothing is visible until you publish via publish_auto_saves (or publish_canvas_page for a whole page). Pending drafts are listed at canvas://auto-saves.
    • No fixed numeric limits; the timeout is the bound. Verification surfaced no request rate limit, no maximum batch_create_nodes batch size, and no request payload cap. The only ceiling is the per-request timeout, so very large batches or queries fail by timing out (-32001) rather than by hitting a documented cap. Split or narrow them: see the failure entries below.
  6. Least privilege lives on the credential, not the connection. Give each agent its own API client with the fewest Allowed Scopes its job needs (the scope catalog); a browser-authorized agent’s ceiling is the person who clicked Allow. One verified caution: scope gating was not observable in MCP server verification (Connection). Treat scopes as the control you set deliberately, and lean on the permission layer, which still refuses and names the missing permission (scopes are not team roles).

    Revocation has two levers and one clock:

    • Toggle Enable MCP Server off. This invalidates the endpoint for every agent on that site, reversibly.
    • Delete the agent’s client at API > API clients. The next token request fails with invalid_client; for zero-downtime swaps, use the rotation order.

    Either way, no already-issued token outlives its 300 seconds.

    Attribution follows the credential: a browser-authorized agent acts as its approver, a client_credentials token acts as the client’s own identity, so per-agent clients are per-agent accountability. And most agent writes land as drafts first: inspect pending work at canvas://auto-saves, then publish_auto_saves or discard_auto_saves before anything reaches readers.

401 Unauthorized on every request, or the client reports the server needs authentication

the OAuth handshake never completed, or the token expired (tokens last 300 seconds) and silent refresh failed. The 401 itself says little: its WWW-Authenticate header carries only Bearer realm="mcp_server", so when diagnosing by hand read the resource metadata at {DRUPAL_SITE_URL}/.well-known/oauth-protected-resource/mcp directly for the scopes and endpoints the server accepts. Re-run your client’s authenticate action (in Claude Code: /mcp → select the server → Authenticate) and finish the browser flow through Allow. If the browser step itself fails, confirm you are signed in to the site in that browser, and that the MCP Server feature is still enabled; disabling it invalidates the endpoint. Deeper OAuth diagnostics: the auth guide’s troubleshooting.

MCP error -32601: Method not found on a tool the reference says exists

your client cached the server’s capabilities from an earlier session, or client and server negotiated mismatched protocol versions. Restart the client; if the tool list is still stale, remove and re-add the server, and update the client; MCP clients pin the protocol versions they support.

Tools are missing from the client’s list even though MCP Inspector shows them

client-side caching. The Inspector talks to the server directly, so if it lists the tool and your client doesn’t, the client’s cached capability list is stale: restart the client or re-add the server. If the Inspector doesn’t show the tool either, the server genuinely doesn’t expose it: check the reference for what’s actually in the catalog.

MCP error -32001: Request timed out

the call was too big: a list_entities sweep over a large site, or a batch_create_nodes payload with too many nodes. Narrow the query (filter by content type, request fewer entities) or split the batch into smaller calls and let the agent iterate.

Was this page helpful?