Skip to content

Commit f425442

Browse files
chore: add mcp auto-discovery in non-production environments
1 parent 7d18bd1 commit f425442

File tree

2 files changed

+28
-19
lines changed

2 files changed

+28
-19
lines changed

README.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ The primary way to configure the MCP server in Laravel is through the `config/mc
6363

6464
## Usage
6565

66-
### 1. Defining MCP Elements
66+
### Defining MCP Elements
6767

6868
Define your MCP Tools, Resources, and Prompts as methods within PHP classes, decorated with attributes from the `php-mcp/server` package (`#[McpTool]`, `#[McpResource]`, `#[McpPrompt]`, etc.).
6969

@@ -102,25 +102,28 @@ class MyTools
102102
* **Dependency Injection:** Your classes' constructors will be resolved using Laravel's service container, so you can inject any application dependencies (like the `LoggerInterface` above).
103103
* **Attribute Usage:** Refer to the [`php-mcp/server` README](https://github.com/php-mcp/server/blob/main/README.md#defining-mcp-elements-with-attributes) for detailed information on defining elements and formatting return values.
104104

105-
### 2. Running Discovery
105+
### Automatic Discovery (Development) vs. Manual Discovery (Production)
106106

107-
Before clients can connect (especially via stdio), the server needs to discover your annotated elements.
107+
The server needs to discover your annotated elements before clients can use them.
108108

109-
```bash
110-
php artisan mcp:discover
111-
```
112-
This command scans the configured directories and caches the found elements using the configured Laravel cache store. It's recommended to run this during deployment or whenever your MCP elements change.
109+
* **Development:** In non-production environments (e.g., `APP_ENV=local`), the server will **automatically discover** elements the first time the MCP server is needed (like on the first relevant HTTP request or Artisan command). You generally **do not** need to run the command manually during development after adding or changing elements.
110+
111+
* **Production:** For performance reasons, automatic discovery is **disabled** in production environments (`APP_ENV=production`). You **must run the discovery command manually** as part of your deployment process:
112+
113+
```bash
114+
php artisan mcp:discover
115+
```
113116

114-
### 3. Running the Server
117+
This command scans the configured directories and caches the found elements using the configured Laravel cache store. Running it during deployment ensures your production environment uses the pre-discovered, cached elements for optimal performance.
118+
119+
*(You can still run `mcp:discover` manually in development if you wish, for example, to pre-populate the cache.)*
120+
121+
### Running the Server
115122

116123
You can expose your MCP server using either the stdio or HTTP+SSE transport.
117124

118125
**Stdio Transport:**
119126

120-
* Run the server using the Artisan command:
121-
```bash
122-
php artisan mcp:serve
123-
```
124127
* Configure your MCP client (Cursor, Claude Desktop) to connect via command. **Important:** Ensure you provide the **full path** to your project's `artisan` file.
125128
126129
*Example Client Config (e.g., `.cursor/mcp.json`):*
@@ -142,7 +145,7 @@ You can expose your MCP server using either the stdio or HTTP+SSE transport.
142145
**HTTP+SSE Transport:**
143146
144147
* **Enable:** Ensure `transports.http.enabled` is `true` in `config/mcp.php`.
145-
* **Routes:** The package automatically registers two routes (by default `/mcp/sse` [GET] and `/mcp` [POST]) using the configured prefix and middleware (`web` by default).
148+
* **Routes:** The package automatically registers two routes (by default `/mcp/sse` [GET] and `/mcp/message` [POST]) using the configured prefix and middleware (`web` by default).
146149
*
147150
* **Web Server Environment (CRITICAL):**
148151
* The built-in `php artisan serve` development server **cannot reliably handle** the concurrent nature of SSE (long-running GET request) and subsequent POST requests from the MCP client. This is because it runs as a single PHP process. You will likely encounter hangs or requests not being processed.
@@ -189,7 +192,7 @@ You can expose your MCP server using either the stdio or HTTP+SSE transport.
189192
```
190193
The server will automatically inform the client about the correct POST endpoint URL (including a unique `?clientId=...` query parameter) via the initial `endpoint` event sent over the SSE connection.
191194

192-
### 4. Other Commands
195+
### Other Commands
193196

194197
* **List Elements:** View the discovered MCP elements.
195198
```bash
@@ -203,7 +206,7 @@ You can expose your MCP server using either the stdio or HTTP+SSE transport.
203206
php artisan mcp:list --json
204207
```
205208

206-
### 5. Dynamic Updates (Notifications)
209+
### Dynamic Updates (Notifications)
207210

208211
If the list of available tools, resources, or prompts changes while the server is running, or if a specific resource's content is updated, you can notify connected clients (primarily useful for HTTP+SSE).
209212

src/McpServiceProvider.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,23 @@ public function register(): void
5252
$cacheStore = $app['cache']->store($config->get('mcp.cache.store'));
5353
$logger = $app['log']->channel($config->get('mcp.logging.channel'));
5454

55-
return Server::make()
55+
$server = Server::make()
5656
->withContainer($app)
5757
->withConfig($mcpConfig)
5858
->withBasePath(base_path())
5959
->withLogger($logger)
6060
->withCache($cacheStore);
61+
62+
if (! $this->app->environment('production')) {
63+
$server->discover();
64+
}
65+
66+
return $server;
6167
});
6268

63-
$this->app->singleton(Processor::class, fn (Application $app) => $app->make(Server::class)->getProcessor());
64-
$this->app->singleton(Registry::class, fn (Application $app) => $app->make(Server::class)->getRegistry());
65-
$this->app->singleton(TransportState::class, fn (Application $app) => $app->make(Server::class)->getStateManager());
69+
$this->app->bind(Processor::class, fn (Application $app) => $app->make(Server::class)->getProcessor());
70+
$this->app->bind(Registry::class, fn (Application $app) => $app->make(Server::class)->getRegistry());
71+
$this->app->bind(TransportState::class, fn (Application $app) => $app->make(Server::class)->getStateManager());
6672

6773
$this->app->bind(HttpTransportHandler::class, function (Application $app) {
6874
return new HttpTransportHandler(

0 commit comments

Comments
 (0)