Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,23 +190,42 @@ $response = $server->run($transport);
By default, the SDK uses in-memory sessions. You can configure different session stores:

```php
use Mcp\Server\Session\InMemorySessionStore;
use Mcp\Server\Session\FileSessionStore;
use Mcp\Server\Session\InMemorySessionStore;
use Mcp\Server\Session\Psr16StoreSession;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\Cache\Adapter\RedisAdapter;

// Use default in-memory sessions (TTL only)
// Use default in-memory sessions with custom TTL
$server = Server::builder()
->setSession(ttl: 7200) // 2 hours
->build();

// Use file-based sessions
// Override with file-based storage
$server = Server::builder()
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
->build();

// Use in-memory with custom TTL
// Override with in-memory storage and custom TTL
$server = Server::builder()
->setSession(new InMemorySessionStore(3600))
->build();

// Override with PSR-16 cache-based storage
// Requires psr/simple-cache and symfony/cache (or any other PSR-16 implementation)
// composer require psr/simple-cache symfony/cache
$redisAdapter = new RedisAdapter(
RedisAdapter::createConnection('redis://localhost:6379'),
'mcp_sessions'
);

$server = Server::builder()
->setSession(new Psr16StoreSession(
cache: new Psr16Cache($redisAdapter),
prefix: 'mcp-',
ttl: 3600
))
->build();
```

### Discovery Caching
Expand Down
22 changes: 21 additions & 1 deletion docs/server-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ Configure session storage and lifecycle. By default, the SDK uses `InMemorySessi
```php
use Mcp\Server\Session\FileSessionStore;
use Mcp\Server\Session\InMemorySessionStore;
use Mcp\Server\Session\Psr16StoreSession;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\Cache\Adapter\RedisAdapter;

// Use default in-memory sessions with custom TTL
$server = Server::builder()
Expand All @@ -147,18 +150,35 @@ $server = Server::builder()

// Override with file-based storage
$server = Server::builder()
->setSession(new FileSessionStore('/tmp/mcp-sessions'))
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
->build();

// Override with in-memory storage and custom TTL
$server = Server::builder()
->setSession(new InMemorySessionStore(3600))
->build();

// Override with PSR-16 cache-based storage
// Requires psr/simple-cache and symfony/cache (or any other PSR-16 implementation)
// composer require psr/simple-cache symfony/cache
$redisAdapter = new RedisAdapter(
RedisAdapter::createConnection('redis://localhost:6379'),
'mcp_sessions'
);

$server = Server::builder()
->setSession(new Psr16StoreSession(
cache: new Psr16Cache($redisAdapter),
prefix: 'mcp-',
ttl: 3600
))
->build();
```

**Available Session Stores:**
- `InMemorySessionStore`: Fast in-memory storage (default)
- `FileSessionStore`: Persistent file-based storage
- `Psr16StoreSession`: PSR-16 compliant cache-based storage

**Custom Session Stores:**

Expand Down
81 changes: 81 additions & 0 deletions src/Server/Session/Psr16StoreSession.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Mcp\Server\Session;

use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Uid\Uuid;

/**
* @author luoyue <[email protected]>
*
* PSR-16 compliant cache-based session store.
*
* This implementation uses any PSR-16 compliant cache as the storage backend
* for session data. Each session is stored with a prefixed key using the session ID.
*/
class Psr16StoreSession implements SessionStoreInterface
{
public function __construct(
private readonly CacheInterface $cache,
private readonly string $prefix = 'mcp-',
private readonly int $ttl = 3600,
) {
}

public function exists(Uuid $id): bool
{
try {
return $this->cache->has($this->getKey($id));
} catch (\Throwable) {
return false;
}
}

public function read(Uuid $id): string|false
{
try {
return $this->cache->get($this->getKey($id), false);
} catch (\Throwable) {
return false;
}
}

public function write(Uuid $id, string $data): bool
{
try {
return $this->cache->set($this->getKey($id), $data, $this->ttl);
} catch (\Throwable) {
return false;
}
}

public function destroy(Uuid $id): bool
{
try {
return $this->cache->delete($this->getKey($id));
} catch (\Throwable) {
return false;
}
}

public function gc(): array
{
return [];
}

private function getKey(Uuid $id): string
{
return $this->prefix.$id;
}
}