Skip to content

Commit f41ea86

Browse files
authored
feat(session): implement PSR-16 cache based session storage (#114)
1 parent 7190bee commit f41ea86

File tree

3 files changed

+125
-5
lines changed

3 files changed

+125
-5
lines changed

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,42 @@ $response = $server->run($transport);
190190
By default, the SDK uses in-memory sessions. You can configure different session stores:
191191

192192
```php
193-
use Mcp\Server\Session\InMemorySessionStore;
194193
use Mcp\Server\Session\FileSessionStore;
194+
use Mcp\Server\Session\InMemorySessionStore;
195+
use Mcp\Server\Session\Psr16StoreSession;
196+
use Symfony\Component\Cache\Psr16Cache;
197+
use Symfony\Component\Cache\Adapter\RedisAdapter;
195198

196-
// Use default in-memory sessions (TTL only)
199+
// Use default in-memory sessions with custom TTL
197200
$server = Server::builder()
198201
->setSession(ttl: 7200) // 2 hours
199202
->build();
200203

201-
// Use file-based sessions
204+
// Override with file-based storage
202205
$server = Server::builder()
203206
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
204207
->build();
205208

206-
// Use in-memory with custom TTL
209+
// Override with in-memory storage and custom TTL
207210
$server = Server::builder()
208211
->setSession(new InMemorySessionStore(3600))
209212
->build();
213+
214+
// Override with PSR-16 cache-based storage
215+
// Requires psr/simple-cache and symfony/cache (or any other PSR-16 implementation)
216+
// composer require psr/simple-cache symfony/cache
217+
$redisAdapter = new RedisAdapter(
218+
RedisAdapter::createConnection('redis://localhost:6379'),
219+
'mcp_sessions'
220+
);
221+
222+
$server = Server::builder()
223+
->setSession(new Psr16StoreSession(
224+
cache: new Psr16Cache($redisAdapter),
225+
prefix: 'mcp-',
226+
ttl: 3600
227+
))
228+
->build();
210229
```
211230

212231
### Discovery Caching

docs/server-builder.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ Configure session storage and lifecycle. By default, the SDK uses `InMemorySessi
139139
```php
140140
use Mcp\Server\Session\FileSessionStore;
141141
use Mcp\Server\Session\InMemorySessionStore;
142+
use Mcp\Server\Session\Psr16StoreSession;
143+
use Symfony\Component\Cache\Psr16Cache;
144+
use Symfony\Component\Cache\Adapter\RedisAdapter;
142145

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

148151
// Override with file-based storage
149152
$server = Server::builder()
150-
->setSession(new FileSessionStore('/tmp/mcp-sessions'))
153+
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
151154
->build();
152155

153156
// Override with in-memory storage and custom TTL
154157
$server = Server::builder()
155158
->setSession(new InMemorySessionStore(3600))
156159
->build();
160+
161+
// Override with PSR-16 cache-based storage
162+
// Requires psr/simple-cache and symfony/cache (or any other PSR-16 implementation)
163+
// composer require psr/simple-cache symfony/cache
164+
$redisAdapter = new RedisAdapter(
165+
RedisAdapter::createConnection('redis://localhost:6379'),
166+
'mcp_sessions'
167+
);
168+
169+
$server = Server::builder()
170+
->setSession(new Psr16StoreSession(
171+
cache: new Psr16Cache($redisAdapter),
172+
prefix: 'mcp-',
173+
ttl: 3600
174+
))
175+
->build();
157176
```
158177

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

163183
**Custom Session Stores:**
164184

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the official PHP MCP SDK.
7+
*
8+
* A collaboration between Symfony and the PHP Foundation.
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Mcp\Server\Session;
15+
16+
use Psr\SimpleCache\CacheInterface;
17+
use Symfony\Component\Uid\Uuid;
18+
19+
/**
20+
* @author luoyue <1569097443@qq.com>
21+
*
22+
* PSR-16 compliant cache-based session store.
23+
*
24+
* This implementation uses any PSR-16 compliant cache as the storage backend
25+
* for session data. Each session is stored with a prefixed key using the session ID.
26+
*/
27+
class Psr16StoreSession implements SessionStoreInterface
28+
{
29+
public function __construct(
30+
private readonly CacheInterface $cache,
31+
private readonly string $prefix = 'mcp-',
32+
private readonly int $ttl = 3600,
33+
) {
34+
}
35+
36+
public function exists(Uuid $id): bool
37+
{
38+
try {
39+
return $this->cache->has($this->getKey($id));
40+
} catch (\Throwable) {
41+
return false;
42+
}
43+
}
44+
45+
public function read(Uuid $id): string|false
46+
{
47+
try {
48+
return $this->cache->get($this->getKey($id), false);
49+
} catch (\Throwable) {
50+
return false;
51+
}
52+
}
53+
54+
public function write(Uuid $id, string $data): bool
55+
{
56+
try {
57+
return $this->cache->set($this->getKey($id), $data, $this->ttl);
58+
} catch (\Throwable) {
59+
return false;
60+
}
61+
}
62+
63+
public function destroy(Uuid $id): bool
64+
{
65+
try {
66+
return $this->cache->delete($this->getKey($id));
67+
} catch (\Throwable) {
68+
return false;
69+
}
70+
}
71+
72+
public function gc(): array
73+
{
74+
return [];
75+
}
76+
77+
private function getKey(Uuid $id): string
78+
{
79+
return $this->prefix.$id;
80+
}
81+
}

0 commit comments

Comments
 (0)