Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Mcp Oauth Bundle Laravel Package

omouren/mcp-oauth-bundle

A PHP bundle that adds OAuth support to Model Context Protocol (MCP) servers, providing configurable authorization endpoints and token handling to help secure MCP clients without building OAuth plumbing from scratch.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the repo before the pitch. The description claims "Laravel-native configuration and middleware" and "Artisan commands" — the repository itself is a Symfony bundle (omouren/mcp-oauth-bundle) built on top of mcp-bundle and league/oauth2-server-bundle. There is no vendor:publish, no service provider, no Artisan command. If you are a Laravel developer, the honest framing is: this package is a reference implementation of the MCP OAuth specs that you will either (a) run as a Symfony service next to your Laravel app, or (b) read as a conformance checklist and reimplement in Laravel on Passport.

Where to look first, in order:

  1. README.md — the only prose that exists; expect it to be short given 2 stars.
  2. DependencyInjection/Configuration.php — the Symfony config tree is the real documentation. Every knob the bundle accepts is declared here, and the 1.2 release ("add login redirect query parameter in config") confirms that config changes are the primary API surface.
  3. Resources/config/routing*.yaml (or route attributes) — this tells you which endpoints the bundle actually adds: the /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource discovery documents, and the authorize/token/registration routes.
  4. The service definitions — that's where the extension points (resource validators, client repositories) are wired.

Minimal setup (Symfony side):

composer require omouren/mcp-oauth-bundle
// config/bundles.php
return [
    // ...
    Omouren\McpOAuthBundle\McpOAuthBundle::class => ['all' => true],
];
# config/packages/mcp_oauth.yaml  (keys: confirm against Configuration.php)
mcp_oauth:
    issuer: '%env(MCP_ISSUER)%'
    login_redirect: { enabled: true, query_parameter: 'redirect' }

Then point an MCP client (MCP Inspector is the fastest loop) at your MCP endpoint and watch it walk discovery → authorize → token → authenticated call.

First use case for a Laravel developer: rather than adopting the bundle, serve the discovery document from your Laravel app and use the bundle's output as the shape you must match.

Route::get('/.well-known/oauth-protected-resource', fn () => [
    'resource' => config('app.url').'/mcp',
    'authorization_servers' => [config('app.url')],
    'bearer_methods_supported' => ['header'],
]);

Implementation Patterns

Discovery-as-contract. Treat the /.well-known JSON as an interface you can snapshot in tests. Http::get('/.well-known/oauth-protected-resource')->assertJsonPath('resource', ...) catches accidental issuer/host drift, which is the most common breakage.

Two topologies, pick deliberately.

  • Split: Symfony runs the MCP resource server with this bundle; your Laravel app runs the authorization server on Passport (league/oauth2-server under the hood — same engine, different glue). Tokens are validated on the Symfony side by introspection or JWKS.
  • Laravel-only: skip the bundle; implement discovery, resource indicators, and a bearer middleware in Laravel. Budget a day or two — the spec surface is small.

Bearer middleware on MCP routes is where day-to-day work actually lives:

Route::middleware(['auth.bearer:mcp.read'])->post('/mcp', McpController::class);

The middleware must do four things, in order: resolve the token, check exp, check that the token's audience/resource claim exactly equals the MCP server URL, then check scopes. Returning 401 with WWW-Authenticate: Bearer resource_metadata="https://…/.well-known/oauth-protected-resource" is what makes clients auto-discover instead of dead-ending.

Resource-bound tokens (RFC 8707). One token per MCP server. If you front two MCP servers, issue two tokens — never let a token minted for server A pass validation at server B. Enforce it on the authorization request (resource param) and on validation; the bundle's enforcement is only useful if you don't hand out a broad token elsewhere.

CIMD (Client ID Metadata Document). Clients identify themselves with an HTTPS URL to a JSON metadata document rather than a pre-registered client_id. For a Laravel AS this means: fetch the URL with a short timeout, cache it, allowlist hosts, and reject redirects to private address space (classic SSRF). This is the piece that needs the most of your security review time.

Stage the rollout so failures are isolatable: discovery only → discovery + validation → enable CIMD → enforce resource binding. Enabling all four at once means every 401 is a guessing game.

Gotchas and Tips

  • The package docs are thin and partly wrong. Any walkthrough telling you to run php artisan vendor:publish for this package is hallucinated (including the login-redirect assessment in circulation). Verify against composer.json and the config tree.
  • 1.2 is a config-only release. The login redirect parameter exists so an unauthenticated MCP/login flow can return the user to the right place. Treat the redirect value as hostile input: validate host/scheme, reject protocol-relative //evil.example, prefer relative paths. Never redirect()->to($request->query('redirect')) raw.
  • Proxy-host mismatch is the classic silent failure. Discovery documents are absolute URLs. Behind an ingress without trusted proxies configured, you generate http://internal-host/..., the client rejects it, and nothing appears in your app log. Check X-Forwarded-* handling before you debug anything else.
  • Audience mismatch is failure #1 for tokens. The resource the client requested and the URL it's calling must match character-for-character — trailing slash, port, and scheme included.
  • Clients cache discovery and registration aggressively. "It worked yesterday" usually means stale client-side cache. Clear the MCP client's state before changing server config.
  • Persistence of signing keys. league/oauth2-server encryption keys must survive deploys or every refresh token dies at once. If your Laravel app rotates them per container, pin them via env/secret.
  • Pin the version. 2 stars, terse release notes, 2026-09-02 last release — the API is not stabilized. composer require omouren/mcp-oauth-bundle:1.2.*.
  • Token lifetime vs. session length. MCP sessions are long-lived; short access tokens without working refresh produce mid-session 401s that look like random disconnects.
  • Extension over forking. Symfony bundles in this space typically expose interfaces you can override via services.yaml (resource validator, client repository). Check the bundle's service definitions for the tag/alias before you copy code.
  • Debug logging: log the parsed authorization request params, the served discovery document, and the outgoing WWW-Authenticate header. Never log tokens or full authorize URLs with codes.
  • Test matrix: no token, malformed token, expired token, wrong audience, insufficient scope, valid token — plus discovery served over the right host. Six tests will save you a week.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console