Model Context Protocol
MCP is a new protocol, launched in November 2024 by Anthropic, designed to make structured data explorable and actionable via natural language. Grafbase offers MCP support out of the box - removing the need to stand up your own standalone MCP server, configure authentication, or fine-tune access control.
The Grafbase MCP server can be started with the Grafbase CLI by running:
npx grafbase mcp <url>
The MCP server listens to requests at http://127.0.0.1:5000/mcp
by default. To add it to Cursor, create a .cursor/mcp.json
file in your project with the following:
{
"mcpServers": {
"my-graphql-api": {
"url": "http://127.0.0.1:5000/mcp"
}
}
}
The Grafbase Gateway can be configured to expose a MCP endpoint with the following grafbase.toml
configuration:
[mcp]
enabled = true # defaults to false
# Path at which to expose the MCP service
path = "/mcp"
# Whether mutations can be executed
execute_mutations = false
# Either "http-streaming" or "sse". The default is "http-streaming".
transport = "http-streaming"
This gives you an MCP endpoint that exposes the relevant tools to explore and query your GraphQL API. When executing GraphQL requests, all HTTP headers are forwarded from the MCP request, as if you were querying the /graphql
endpoint on your Gateway with a regular GraphQL client. That means that authentication and authorization are enforced in exactly the same way. But if you plan to expose this MCP endpoint as a spec-compliant MCP server, you will need to configure the Gateway to implement the Authorization section of the MCP spec.
The MCP spec defines an OAuth 2.1 based authorization flow. The MCP endpoint enabled with the configuration in the previous section must be secured as an OAuth 2 protected resource. You will also need an external OAuth 2 authorization server, as part of your own infrastructure or SaaS authentication solution. See for example the WorkOS AuthKit docs. The following sections walk you through the features to be addressed:
Authentication is handled the same way as for your GraphQL API endpoint: with extensions.
You can either secure both the MCP endpoint and your GraphQL endpoint with the same authentication extension:
[extensions.jwt]
version = "1"
config.url = "https://example.com/sso/jwks"
Or separately for the MCP endpoint and the GraphQL endpoint:
[authentication.protected_resources.graphql]
extensions = []
default = "anonymous" # superfluous here, it's the default with no extension
[authentication.protected_resources.mcp]
extensions = ["jwt"]
default = "deny" # matches the default when at least one extension is defined
[extensions.jwt]
version = "1"
config.url = "https://example.com/sso/jwks"
For more details, see the configuration reference section on authentication.
The MCP server must expose metadata as defined in RFC-9728. Concretely, it is a static JSON document served at a standard defined path ("/.well-known/oauth-protected-resource"), or an arbitrary path in combination with the resource_metadata
field of the WWW-Authenticate
header (see below).
The open source authentication extensions on the Marketplace all implement OAuth Protected Resource metadata through configuration. See the README of the jwt extension for example.
If you implement authentication with your own extension, you will only need to implement the public_metadata()
method on AuthenticationExtension
. See how the jwt extension does it for a working example.
The MCP spec requires the presence of the WWW-Authenticate header in the response to unauthenticated requests. This header should contain the value "Bearer" to indicate that the resource is protected by OAuth 2.0.
Again, authentication extensions from the marketplace all let you configure what is returned in that header, and you can implement that in your own extensions with ErrorResponse::with_header()
.
If you want to expose your MCP server to clients inside web browsers, you will need to include the mcp-protocol-version
header in allow_headers
. Here is an example of what it looks like in your Gateway configuration (grafbase.toml
):
[cors]
allow_origins = "any"
allow_methods = ["GET", "POST"]
allow_headers = [
"authorization",
"content-type",
"x-grafbase-client-name",
"x-grafbase-client-version",
"mcp-protocol-version"
]