Skip to content

Latest commit

 

History

History
127 lines (102 loc) · 3.93 KB

File metadata and controls

127 lines (102 loc) · 3.93 KB

Session Management

Sessions belong to the handshake era. Protocol revision 2026-07-28 removed them, along with Mcp-Session-Id, because every request carries what is needed to answer it — see The 2026-07-28 lifecycle. One server serves both, so the configuration below still applies to the handshake-era clients it answers.

Configure session storage and lifecycle. By default, the SDK uses InMemorySessionStore:

use Mcp\Server\Session\FileSessionStore;
use Mcp\Server\Session\InMemorySessionStore;
use Mcp\Server\Session\Psr16SessionStore;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\Cache\Adapter\RedisAdapter;

// Override with file-based storage
$server = Server::builder()
    ->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 Psr16SessionStore(
        cache: new Psr16Cache($redisAdapter),
        prefix: 'mcp-',
        ttl: 3600
    ))
    ->build();

Garbage Collection Configuration

The SDK periodically runs garbage collection to clean up expired sessions, similar to PHP's native session.gc_probability and session.gc_divisor settings. The probability that GC runs on any given request is gcProbability / gcDivisor.

// Default: 1/100 (1% chance per request)
$server = Server::builder()
    ->setSession(new FileSessionStore(__DIR__ . '/sessions'))
    ->build();

// Higher frequency: 1/10 (10% chance per request)
$server = Server::builder()
    ->setSession(
        new FileSessionStore(__DIR__ . '/sessions'),
        gcProbability: 1,
        gcDivisor: 10,
    )
    ->build();

// Run GC on every request
$server = Server::builder()
    ->setSession(gcProbability: 1, gcDivisor: 1)
    ->build();

// Disable GC entirely (e.g. when using an external cleanup process)
$server = Server::builder()
    ->setSession(gcProbability: 0)
    ->build();

Parameters:

  • $gcProbability (int): The numerator of the GC probability fraction (default: 1). Set to 0 to disable GC.
  • $gcDivisor (int): The denominator of the GC probability fraction (default: 100). Must be >= 1.

Note: When providing a custom SessionManagerInterface via the $sessionManager parameter, the gcProbability and gcDivisor settings are ignored — you control GC behavior in your own implementation.

Available Session Stores:

  • InMemorySessionStore: Fast in-memory storage (default)
  • FileSessionStore: Persistent file-based storage
  • Psr16SessionStore: PSR-16 compliant cache-based storage

Custom Session Stores:

Implement SessionStoreInterface to create custom session storage:

use Mcp\Server\Session\SessionStoreInterface;
use Symfony\Component\Uid\Uuid;

class RedisSessionStore implements SessionStoreInterface
{
    public function __construct(private $redis, private int $ttl = 3600) {}

    public function exists(Uuid $id): bool
    {
        return $this->redis->exists($id->toRfc4122());
    }

    public function read(Uuid $sessionId): string|false
    {
        $data = $this->redis->get($sessionId->toRfc4122());
        return $data !== false ? $data : false;
    }

    public function write(Uuid $sessionId, string $data): bool
    {
        return $this->redis->setex($sessionId->toRfc4122(), $this->ttl, $data);
    }

    public function destroy(Uuid $sessionId): bool
    {
        return $this->redis->del($sessionId->toRfc4122()) > 0;
    }

    public function gc(): array
    {
        // Redis handles TTL automatically
        return [];
    }
}