diff --git a/.changeset/nasty-hounds-rhyme.md b/.changeset/nasty-hounds-rhyme.md new file mode 100644 index 0000000000..b29ce4ede2 --- /dev/null +++ b/.changeset/nasty-hounds-rhyme.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Added the `mcp-actions-backend` and `plugin-auth` plugins diff --git a/docs/ai/mcp-actions.md b/docs/ai/mcp-actions.md index 498cd42df2..210167e8ae 100644 --- a/docs/ai/mcp-actions.md +++ b/docs/ai/mcp-actions.md @@ -44,10 +44,14 @@ You can configure the name and description of your Backstage MCP server with the ```yaml title="app-config.yaml" mcpActions: - name: 'My MCP Server' # defaults to "backstage" - description: 'Tools for interacting with My MCP Server' # optional + name: 'My Company Backstage' # defaults to "backstage" + description: 'Tools for managing your software catalog, creating new services from templates, and exploring your developer portal' # optional ``` +:::tip +Keep the following in mind when picking the name and description. The description should answer "what can I do with these tools?" from the perspective of an AI agent deciding whether to use this server — not "what is this server?". That means describing Backstage capabilities (catalog, scaffolder, etc.), not the MCP protocol or server identity. +::: + ## Namespaced Tool Names By default, MCP tool names include the plugin ID prefix to avoid collisions across plugins. For example, an action registered as `greet-user` by `my-custom-plugin` is exposed as `my-custom-plugin.greet-user`. @@ -146,20 +150,20 @@ Authorization: Bearer For more details about external access tokens and service-to-service authentication, see the [Service-to-Service Auth documentation](../auth/service-to-service-auth.md). -### Experimental: Dynamic Client Registration +### Experimental Authentication methods -:::warning -This feature is highly experimental and only works with the New Frontend System. Proceed with caution. -::: +The MCP Actions Backend supports two experimental authentication methods based on the MCP specification: -You can configure the auth-backend and install the auth frontend plugin to enable **Dynamic Client Registration** with MCP clients. This means you do not need to manually configure a token in your MCP client settings. Instead, a client can request a token on your behalf. When adding the MCP server to an MCP client like Cursor or Claude, a popup requiring your approval will open in your Backstage instance (powered by the auth plugin). +- [Client ID Metadata Documents (CIMD)](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-id-metadata-documents) +- [Dynamic Client Registration (DCR)](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#dynamic-client-registration) -**Requirements:** +They have the following requirements: +- You must be using the [New Frontend System](../frontend-system/architecture/00-index.md). - The `@backstage/plugin-auth-backend` plugin must be configured. - The new `@backstage/plugin-auth` frontend plugin must be configured. -**Installation:** +Follow these steps to install and configure the new `@backstage/plugin-auth` frontend plugin: 1. Install the `@backstage/plugin-auth` frontend plugin: @@ -180,17 +184,51 @@ You can configure the auth-backend and install the auth frontend plugin to enabl }); ``` -3. Enable the feature: +#### Client ID Metadata Documents - ```yaml title="app-config.yaml" - auth: - experimentalDynamicClientRegistration: - enabled: true +:::warning +This feature is highly experimental; proceed with caution. Client support is also currently limited but quickly being implemented. +::: - # Optional: limit valid callback URLs for added security - allowedRedirectUriPatterns: - - cursor://* - ``` +The [November 2025 MCP specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) outlined a new authorization method to replace Dynamic Client Registration called [Client ID Metadata Documents (CIMD)](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-id-metadata-documents). + +Using Client ID Metadata Documents means you do not need to manually configure a token in your MCP client settings. Instead, a client can request a token on your behalf. When adding the MCP server to an MCP client like Cursor or Claude, a popup requiring your approval will open in your Backstage instance (powered by the `auth` plugin). + +This can be enabled in the `auth-backend` plugin by using the `auth.experimentalClientIdMetadataDocuments.enabled` flag in config: + +```yaml title="app-config.yaml" +auth: + experimentalClientIdMetadataDocuments: + enabled: true + # Optional: restrict which `client_id` URLs are allowed (defaults to ['*']) + allowedClientIdPatterns: + - 'https://example.com/*' + - 'https://*.trusted-domain.com/*' + # Optional: restrict which redirect URIs are allowed (defaults to ['*']) + allowedRedirectUriPatterns: + - 'http://localhost:*' + - 'https://*.example.com/*' +``` + +#### Dynamic Client Registration + +:::warning +This feature is highly experimental; proceed with caution. This method will likely be deprecated and replaced by [Client ID Metadata Documents](#client-id-metadata-documents) in the future. Only use in cases where clients do not yet support Client ID Metadata Documents. +::: + +Using Dynamic Client Registration means you do not need to manually configure a token in your MCP client settings. Instead, a client can request a token on your behalf. When adding the MCP server to an MCP client like Cursor or Claude, a popup requiring your approval will open in your Backstage instance (powered by the `auth` plugin). + +This can be enabled in the `auth-backend` plugin by using the `auth.experimentalDynamicClientRegistration.enabled` flag in config: + +```yaml title="app-config.yaml" +auth: + experimentalDynamicClientRegistration: + enabled: true + + # Optional: limit valid callback URLs for added security + allowedRedirectUriPatterns: + - cursor://* +``` ## Configuring MCP Clients diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index 6416681b27..50f9d18e26 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -83,6 +83,7 @@ jest.mock('./versions', () => ({ '@backstage/e2e-test-utils': '1.0.0', '@backstage/integration-react': '1.0.0', '@backstage/plugin-api-docs': '1.0.0', + '@backstage/plugin-auth': '1.0.0', '@backstage/plugin-catalog': '1.0.0', '@backstage/plugin-catalog-common': '1.0.0', '@backstage/plugin-catalog-graph': '1.0.0', @@ -90,6 +91,7 @@ jest.mock('./versions', () => ({ '@backstage/plugin-catalog-react': '1.0.0', '@backstage/plugin-kubernetes': '1.0.0', '@backstage/plugin-kubernetes-backend': '1.0.0', + '@backstage/plugin-mcp-actions-backend': '1.0.0', '@backstage/plugin-notifications': '1.0.0', '@backstage/plugin-notifications-backend': '1.0.0', '@backstage/plugin-org': '1.0.0', diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 006dd6d7b8..be60a95526 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -57,6 +57,7 @@ import { version as pluginApiDocs } from '../../../../plugins/api-docs/package.j import { version as pluginAppVisualizer } from '../../../../plugins/app-visualizer/package.json'; import { version as pluginAppBackend } from '../../../../plugins/app-backend/package.json'; import { version as pluginAppReact } from '../../../../plugins/app-react/package.json'; +import { version as pluginAuth } from '../../../../plugins/auth/package.json'; import { version as pluginAuthBackend } from '../../../../plugins/auth-backend/package.json'; import { version as pluginAuthBackendModuleGithubProvider } from '../../../../plugins/auth-backend-module-github-provider/package.json'; import { version as pluginAuthBackendModuleGuestProvider } from '../../../../plugins/auth-backend-module-guest-provider/package.json'; @@ -71,6 +72,7 @@ import { version as pluginCatalogGraph } from '../../../../plugins/catalog-graph import { version as pluginCatalogImport } from '../../../../plugins/catalog-import/package.json'; import { version as pluginKubernetes } from '../../../../plugins/kubernetes/package.json'; import { version as pluginKubernetesBackend } from '../../../../plugins/kubernetes-backend/package.json'; +import { version as pluginMcpActionsBackend } from '../../../../plugins/mcp-actions-backend/package.json'; import { version as pluginNotifications } from '../../../../plugins/notifications/package.json'; import { version as pluginNotificationsBackend } from '../../../../plugins/notifications-backend/package.json'; import { version as pluginOrg } from '../../../../plugins/org/package.json'; @@ -123,6 +125,7 @@ export const packageVersions = { '@backstage/plugin-app-backend': pluginAppBackend, '@backstage/plugin-app-react': pluginAppReact, '@backstage/plugin-app-visualizer': pluginAppVisualizer, + '@backstage/plugin-auth': pluginAuth, '@backstage/plugin-auth-backend': pluginAuthBackend, '@backstage/plugin-auth-backend-module-github-provider': pluginAuthBackendModuleGithubProvider, @@ -141,6 +144,7 @@ export const packageVersions = { '@backstage/plugin-catalog-import': pluginCatalogImport, '@backstage/plugin-kubernetes': pluginKubernetes, '@backstage/plugin-kubernetes-backend': pluginKubernetesBackend, + '@backstage/plugin-mcp-actions-backend': pluginMcpActionsBackend, '@backstage/plugin-notifications': pluginNotifications, '@backstage/plugin-notifications-backend': pluginNotificationsBackend, '@backstage/plugin-org': pluginOrg, diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index ca52ec530c..867871ca1e 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -30,6 +30,12 @@ backend: database: client: better-sqlite3 connection: ':memory:' + # see https://backstage.io/docs/ai/mcp-actions#actions-configuration for more details + actions: + pluginSources: + - auth + - catalog + - scaffolder # workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir integrations: @@ -67,6 +73,18 @@ auth: providers: # See https://backstage.io/docs/auth/guest/provider guest: {} + # see https://backstage.io/docs/ai/mcp-actions#client-id-metadata-documents + # to learn more about client id metadata documents + experimentalClientIdMetadataDocuments: + enabled: false + # Optional: restrict which `client_id` URLs are allowed (defaults to ['*']) + # allowedClientIdPatterns: + # - 'https://example.com/*' + # - 'https://*.trusted-domain.com/*' + # Optional: restrict which redirect URIs are allowed (defaults to ['*']) + # allowedRedirectUriPatterns: + # - 'http://localhost:*' + # - 'https://*.example.com/*' scaffolder: # see https://backstage.io/docs/features/software-templates/configuration for software template options @@ -111,3 +129,8 @@ kubernetes: permission: # setting this to `false` will disable permissions enabled: true + +# see https://backstage.io/docs/ai/mcp-actions for more details +mcpActions: + name: 'My Company Backstage' # defaults to "backstage" + description: 'Tools for managing your software catalog, creating new services from templates, and exploring your developer portal' # optional diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 518683ffd7..e906dff67d 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -27,6 +27,7 @@ "@backstage/plugin-catalog-backend-module-logs": "^{{version '@backstage/plugin-catalog-backend-module-logs'}}", "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^{{version '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'}}", "@backstage/plugin-kubernetes-backend": "^{{version '@backstage/plugin-kubernetes-backend'}}", + "@backstage/plugin-mcp-actions-backend": "^{{version '@backstage/plugin-mcp-actions-backend'}}", "@backstage/plugin-notifications-backend": "^{{version '@backstage/plugin-notifications-backend'}}", "@backstage/plugin-permission-backend": "^{{version '@backstage/plugin-permission-backend'}}", "@backstage/plugin-permission-backend-module-allow-all-policy": "^{{version '@backstage/plugin-permission-backend-module-allow-all-policy'}}", diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index cc2db7c3d4..7e9be64a7d 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -63,4 +63,7 @@ backend.add(import('@backstage/plugin-kubernetes-backend')); backend.add(import('@backstage/plugin-notifications-backend')); backend.add(import('@backstage/plugin-signals-backend')); +// mcp actions plugin +backend.add(import('@backstage/plugin-mcp-actions-backend')); + backend.start(); diff --git a/packages/create-app/templates/next-app/app-config.yaml.hbs b/packages/create-app/templates/next-app/app-config.yaml.hbs index b26d412a93..f5fbee4130 100644 --- a/packages/create-app/templates/next-app/app-config.yaml.hbs +++ b/packages/create-app/templates/next-app/app-config.yaml.hbs @@ -39,6 +39,12 @@ backend: database: client: better-sqlite3 connection: ':memory:' + # see https://backstage.io/docs/ai/mcp-actions#actions-configuration for more details + actions: + pluginSources: + - auth + - catalog + - scaffolder # workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir integrations: @@ -76,6 +82,25 @@ auth: providers: # See https://backstage.io/docs/auth/guest/provider guest: {} + # see https://backstage.io/docs/ai/mcp-actions#dynamic-client-registration + # to learn more about dynamic client registration + experimentalDynamicClientRegistration: + enabled: false + # Optional: limit valid callback URLs for added security + # allowedRedirectUriPatterns: + # - cursor://* + # see https://backstage.io/docs/ai/mcp-actions#client-id-metadata-documents + # to learn more about client id metadata documents + experimentalClientIdMetadataDocuments: + enabled: true + # Optional: restrict which `client_id` URLs are allowed (defaults to ['*']) + # allowedClientIdPatterns: + # - 'https://example.com/*' + # - 'https://*.trusted-domain.com/*' + # Optional: restrict which redirect URIs are allowed (defaults to ['*']) + # allowedRedirectUriPatterns: + # - 'http://localhost:*' + # - 'https://*.example.com/*' scaffolder: # see https://backstage.io/docs/features/software-templates/configuration for software template options @@ -120,3 +145,8 @@ kubernetes: permission: # setting this to `false` will disable permissions enabled: true + +# see https://backstage.io/docs/ai/mcp-actions for more details +mcpActions: + name: 'My Company Backstage' # defaults to "backstage" + description: 'Tools for managing your software catalog, creating new services from templates, and exploring your developer portal' # optional diff --git a/packages/create-app/templates/next-app/packages/app/package.json.hbs b/packages/create-app/templates/next-app/packages/app/package.json.hbs index b31bafb8d1..54672f6afa 100644 --- a/packages/create-app/templates/next-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/next-app/packages/app/package.json.hbs @@ -23,6 +23,7 @@ "@backstage/plugin-api-docs": "^{{ version '@backstage/plugin-api-docs'}}", "@backstage/plugin-app-react": "^{{ version '@backstage/plugin-app-react'}}", "@backstage/plugin-app-visualizer": "^{{ version '@backstage/plugin-app-visualizer'}}", + "@backstage/plugin-auth": "^{{ version '@backstage/plugin-auth'}}", "@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}", "@backstage/plugin-catalog-graph": "^{{ version '@backstage/plugin-catalog-graph'}}", "@backstage/plugin-catalog-import": "^{{ version '@backstage/plugin-catalog-import'}}", diff --git a/packages/create-app/templates/next-app/packages/backend/package.json.hbs b/packages/create-app/templates/next-app/packages/backend/package.json.hbs index 518683ffd7..e906dff67d 100644 --- a/packages/create-app/templates/next-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/next-app/packages/backend/package.json.hbs @@ -27,6 +27,7 @@ "@backstage/plugin-catalog-backend-module-logs": "^{{version '@backstage/plugin-catalog-backend-module-logs'}}", "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^{{version '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'}}", "@backstage/plugin-kubernetes-backend": "^{{version '@backstage/plugin-kubernetes-backend'}}", + "@backstage/plugin-mcp-actions-backend": "^{{version '@backstage/plugin-mcp-actions-backend'}}", "@backstage/plugin-notifications-backend": "^{{version '@backstage/plugin-notifications-backend'}}", "@backstage/plugin-permission-backend": "^{{version '@backstage/plugin-permission-backend'}}", "@backstage/plugin-permission-backend-module-allow-all-policy": "^{{version '@backstage/plugin-permission-backend-module-allow-all-policy'}}", diff --git a/packages/create-app/templates/next-app/packages/backend/src/index.ts b/packages/create-app/templates/next-app/packages/backend/src/index.ts index cc2db7c3d4..7e9be64a7d 100644 --- a/packages/create-app/templates/next-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/next-app/packages/backend/src/index.ts @@ -63,4 +63,7 @@ backend.add(import('@backstage/plugin-kubernetes-backend')); backend.add(import('@backstage/plugin-notifications-backend')); backend.add(import('@backstage/plugin-signals-backend')); +// mcp actions plugin +backend.add(import('@backstage/plugin-mcp-actions-backend')); + backend.start();