Skip to content

Commit c47aa9d

Browse files
committed
Add HTTP + SSE Transport
1 parent 0555ca5 commit c47aa9d

24 files changed

+966
-403
lines changed

composer.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,16 @@
1414
"php": ">=8.3",
1515
"guzzlehttp/guzzle": "^7.0",
1616
"laminas/laminas-diactoros": "^3.5",
17-
"league/route": "^6.2",
18-
"logiscape/mcp-sdk-php": "^1.0",
17+
"league/route": "~6.0.0",
18+
"spiral/mcp-server": "dev-master",
1919
"psr-discovery/http-client-implementations": "^1.0",
2020
"psr-discovery/http-factory-implementations": "^1.0",
2121
"psr/http-client": "^1.0",
2222
"psr/http-factory": "^1.0",
2323
"spiral/boot": "^3.15",
2424
"spiral/config": "^3.15",
2525
"spiral/console": "^3.15",
26-
"spiral/files": "^3.15",
27-
"spiral/json-schema-generator": "^2.1",
28-
"cuyz/valinor": "^1.7"
26+
"spiral/files": "^3.15"
2927
},
3028
"require-dev": {
3129
"spiral/code-style": "^2.2",

src/Action/ToolResult.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
namespace Butschster\ContextGenerator\McpServer\Action;
66

7-
use Mcp\Types\CallToolResult;
8-
use Mcp\Types\TextContent;
7+
use PhpMcp\Schema\Content\TextContent;
8+
use PhpMcp\Schema\Result\CallToolResult;
99

1010
/**
1111
* Helper class to simplify creating CallToolResult instances for MCP tool actions.

src/McpServerBootloader.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use Butschster\ContextGenerator\McpServer\Registry\McpItemsRegistry;
1212
use Butschster\ContextGenerator\McpServer\Routing\McpResponseStrategy;
1313
use Butschster\ContextGenerator\McpServer\Routing\RouteRegistrar;
14-
use Butschster\ContextGenerator\McpServer\SchemaMapper\SchemaMapperBootloader;
1514
use Butschster\ContextGenerator\McpServer\Tool\McpToolBootloader;
1615
use League\Route\Router;
1716
use League\Route\Strategy\StrategyInterface;
@@ -29,7 +28,7 @@ public function defineDependencies(): array
2928
McpPromptBootloader::class,
3029
McpProjectsBootloader::class,
3130
McpConfigBootloader::class,
32-
SchemaMapperBootloader::class,
31+
McpServerCoreBootloader::class,
3332
];
3433
}
3534

src/McpServerCoreBootloader.php

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Butschster\ContextGenerator\McpServer;
6+
7+
use Butschster\ContextGenerator\McpServer\Routing\RoutesFactory;
8+
use Butschster\ContextGenerator\McpServer\Transport\StdioTransport;
9+
use Mcp\Server\Configuration;
10+
use Mcp\Server\Contracts\DispatcherInterface;
11+
use Mcp\Server\Contracts\DispatcherRoutesFactoryInterface;
12+
use Mcp\Server\Contracts\HttpServerInterface;
13+
use Mcp\Server\Contracts\ReferenceProviderInterface;
14+
use Mcp\Server\Contracts\ReferenceRegistryInterface;
15+
use Mcp\Server\Contracts\ServerTransportInterface;
16+
use Mcp\Server\Contracts\SessionHandlerInterface;
17+
use Mcp\Server\Contracts\SessionIdGeneratorInterface;
18+
use Mcp\Server\Contracts\ToolExecutorInterface;
19+
use Mcp\Server\Defaults\ArrayCache;
20+
use Mcp\Server\Defaults\FileCache;
21+
use Mcp\Server\Defaults\ToolExecutor;
22+
use Mcp\Server\Dispatcher;
23+
use Mcp\Server\Dispatcher\Paginator;
24+
use Mcp\Server\Protocol;
25+
use Mcp\Server\Registry;
26+
use Mcp\Server\Server;
27+
use Mcp\Server\Session\ArraySessionHandler;
28+
use Mcp\Server\Session\CacheSessionHandler;
29+
use Mcp\Server\Session\SessionIdGenerator;
30+
use Mcp\Server\Session\SessionManager;
31+
use Mcp\Server\Session\SubscriptionManager;
32+
use Mcp\Server\Transports\HttpServer;
33+
use Mcp\Server\Transports\HttpServerTransport;
34+
use Mcp\Server\Transports\StdioServerTransport;
35+
use Mcp\Server\Transports\StreamableHttpServerTransport;
36+
use PhpMcp\Schema\Implementation;
37+
use PhpMcp\Schema\ServerCapabilities;
38+
use Psr\Container\ContainerInterface;
39+
use Psr\Log\LoggerInterface;
40+
use Psr\SimpleCache\CacheInterface;
41+
use React\EventLoop\Loop;
42+
use React\EventLoop\LoopInterface;
43+
use Spiral\Boot\Bootloader\Bootloader;
44+
use Spiral\Boot\DirectoriesInterface;
45+
use Spiral\Boot\EnvironmentInterface;
46+
use Spiral\Core\FactoryInterface;
47+
use Spiral\McpServer\Bootloader\ValinorMapperBootloader;
48+
use Spiral\McpServer\MiddlewareManager;
49+
use Spiral\McpServer\MiddlewareRegistryInterface;
50+
use Spiral\McpServer\MiddlewareRepositoryInterface;
51+
52+
final class McpServerCoreBootloader extends Bootloader
53+
{
54+
public function defineDependencies(): array
55+
{
56+
return [
57+
ValinorMapperBootloader::class,
58+
];
59+
}
60+
61+
public function defineSingletons(): array
62+
{
63+
return [
64+
// Core Dependencies
65+
LoopInterface::class => static fn(): LoopInterface => Loop::get(),
66+
67+
// Middleware Management
68+
MiddlewareRepositoryInterface::class => MiddlewareManager::class,
69+
MiddlewareRegistryInterface::class => MiddlewareManager::class,
70+
71+
// Session Management
72+
SessionIdGeneratorInterface::class => SessionIdGenerator::class,
73+
SessionHandlerInterface::class => $this->createSessionHandler(...),
74+
SessionManager::class => $this->createSessionManager(...),
75+
SubscriptionManager::class => $this->createSubscriptionManager(...),
76+
77+
// Cache and Storage
78+
CacheInterface::class => $this->createCache(...),
79+
80+
// Registry and Tools
81+
ReferenceProviderInterface::class => Registry::class,
82+
ReferenceRegistryInterface::class => Registry::class,
83+
Registry::class => $this->createRegistry(...),
84+
ToolExecutorInterface::class => $this->createToolExecutor(...),
85+
86+
// Configuration
87+
Configuration::class => $this->createMcpConfiguration(...),
88+
89+
// Pagination
90+
Paginator::class => $this->createPaginator(...),
91+
92+
// Routing and Dispatch
93+
DispatcherRoutesFactoryInterface::class => RoutesFactory::class,
94+
DispatcherInterface::class => Dispatcher::class,
95+
Dispatcher::class => $this->createDispatcher(...),
96+
97+
// Protocol
98+
Protocol::class => $this->createProtocol(...),
99+
100+
// Transport
101+
HttpServerInterface::class => $this->createHttpServer(...),
102+
ServerTransportInterface::class => $this->createTransport(...),
103+
104+
// Main Server
105+
Server::class => Server::class,
106+
];
107+
}
108+
109+
private function createSessionHandler(
110+
EnvironmentInterface $env,
111+
CacheInterface $cache,
112+
): SessionHandlerInterface {
113+
$sessionType = $env->get('MCP_SESSION_TYPE', 'array');
114+
$ttl = (int)$env->get('MCP_SESSION_TTL', 3600);
115+
116+
return match ($sessionType) {
117+
'cache' => new CacheSessionHandler($cache, $ttl),
118+
default => new ArraySessionHandler($ttl),
119+
};
120+
}
121+
122+
private function createSessionManager(
123+
SessionHandlerInterface $sessionHandler,
124+
LoggerInterface $logger,
125+
LoopInterface $loop,
126+
EnvironmentInterface $env,
127+
): SessionManager {
128+
return new SessionManager(
129+
handler: $sessionHandler,
130+
logger: $logger,
131+
loop: $loop,
132+
ttl: (int)$env->get('MCP_SESSION_TTL', 3600),
133+
gcInterval: (float)$env->get('MCP_SESSION_GC_INTERVAL', 300),
134+
);
135+
}
136+
137+
private function createSubscriptionManager(LoggerInterface $logger): SubscriptionManager
138+
{
139+
return new SubscriptionManager($logger);
140+
}
141+
142+
private function createCache(
143+
EnvironmentInterface $env,
144+
DirectoriesInterface $dirs,
145+
): CacheInterface {
146+
$cacheType = $env->get('MCP_CACHE_TYPE', 'array');
147+
148+
return match ($cacheType) {
149+
'file' => new FileCache($dirs->get('runtime') . 'cache/mcp'),
150+
default => new ArrayCache(),
151+
};
152+
}
153+
154+
private function createRegistry(
155+
LoggerInterface $logger,
156+
): Registry {
157+
return new Registry($logger);
158+
}
159+
160+
private function createToolExecutor(
161+
ReferenceRegistryInterface $registry,
162+
LoggerInterface $logger,
163+
): ToolExecutorInterface {
164+
return new ToolExecutor($registry, $logger);
165+
}
166+
167+
private function createMcpConfiguration(
168+
EnvironmentInterface $env,
169+
): Configuration {
170+
return new Configuration(
171+
serverInfo: Implementation::make(
172+
name: trim((string)$env->get('MCP_SERVER_NAME', 'Spiral MCP Server')),
173+
version: trim((string)$env->get('MCP_SERVER_VERSION', '1.0.0')),
174+
),
175+
capabilities: ServerCapabilities::make(
176+
tools: $env->get('MCP_ENABLE_TOOLS', true),
177+
toolsListChanged: $env->get('MCP_ENABLE_TOOLS_LIST_CHANGED', true),
178+
resources: $env->get('MCP_ENABLE_RESOURCES', true),
179+
resourcesSubscribe: $env->get('MCP_ENABLE_RESOURCES_SUBSCRIBE', true),
180+
resourcesListChanged: $env->get('MCP_ENABLE_RESOURCES_LIST_CHANGED', true),
181+
prompts: $env->get('MCP_ENABLE_PROMPTS', true),
182+
promptsListChanged: $env->get('MCP_ENABLE_PROMPTS_LIST_CHANGED', true),
183+
logging: $env->get('MCP_ENABLE_LOGGING', true),
184+
completions: $env->get('MCP_ENABLE_COMPLETIONS', true),
185+
experimental: (array)$env->get('MCP_EXPERIMENTAL_CAPABILITIES', []),
186+
),
187+
instructions: $env->get('MCP_INSTRUCTIONS'),
188+
);
189+
}
190+
191+
private function createPaginator(EnvironmentInterface $env): Paginator
192+
{
193+
return new Paginator(
194+
paginationLimit: (int)$env->get('MCP_PAGINATION_LIMIT', 50),
195+
);
196+
}
197+
198+
private function createDispatcher(
199+
LoggerInterface $logger,
200+
RoutesFactory $routesFactory,
201+
): Dispatcher {
202+
return new Dispatcher(
203+
logger: $logger,
204+
routesFactory: $routesFactory,
205+
);
206+
}
207+
208+
private function createProtocol(
209+
FactoryInterface $factory,
210+
LoggerInterface $logger,
211+
): Protocol {
212+
return $factory->make(Protocol::class, [
213+
'logger' => $logger,
214+
]);
215+
}
216+
217+
private function createHttpServer(
218+
EnvironmentInterface $env,
219+
LoopInterface $loop,
220+
MiddlewareRepositoryInterface $middleware,
221+
LoggerInterface $logger,
222+
): HttpServerInterface {
223+
$host = $env->get('MCP_HOST', '127.0.0.1');
224+
$port = (int)$env->get('MCP_PORT', 8090);
225+
$mcpPath = $env->get('MCP_PATH', '/mcp');
226+
227+
return new HttpServer(
228+
loop: $loop,
229+
host: $host,
230+
port: $port,
231+
mcpPath: $mcpPath,
232+
sslContext: $env->get('MCP_SSL_CONTEXT'),
233+
middleware: $middleware->all(),
234+
logger: $logger,
235+
runLoop: true,
236+
);
237+
}
238+
239+
private function createTransport(
240+
ContainerInterface $container,
241+
EnvironmentInterface $env,
242+
LoopInterface $loop,
243+
SessionIdGeneratorInterface $sessionIdGenerator,
244+
LoggerInterface $logger,
245+
): ServerTransportInterface {
246+
$transportType = $env->get('MCP_TRANSPORT', 'stdio');
247+
248+
return match ($transportType) {
249+
'http' => $this->createHttpTransport(
250+
httpServer: $container->get(HttpServerInterface::class),
251+
sessionIdGenerator: $sessionIdGenerator,
252+
logger: $logger,
253+
),
254+
'streamable' => $this->createStreamableHttpTransport(
255+
httpServer: $container->get(HttpServerInterface::class),
256+
env: $env,
257+
sessionIdGenerator: $sessionIdGenerator,
258+
logger: $logger,
259+
),
260+
'stdio' => new StdioTransport(
261+
logger: $logger,
262+
),
263+
default => throw new \InvalidArgumentException("Unknown transport type: {$transportType}")
264+
};
265+
}
266+
267+
private function createHttpTransport(
268+
HttpServerInterface $httpServer,
269+
SessionIdGeneratorInterface $sessionIdGenerator,
270+
LoggerInterface $logger,
271+
): HttpServerTransport {
272+
return new HttpServerTransport(
273+
httpServer: $httpServer,
274+
sessionId: $sessionIdGenerator,
275+
logger: $logger,
276+
);
277+
}
278+
279+
private function createStreamableHttpTransport(
280+
HttpServerInterface $httpServer,
281+
EnvironmentInterface $env,
282+
SessionIdGeneratorInterface $sessionIdGenerator,
283+
LoggerInterface $logger,
284+
): StreamableHttpServerTransport {
285+
return new StreamableHttpServerTransport(
286+
httpServer: $httpServer,
287+
sessionId: $sessionIdGenerator,
288+
logger: $logger,
289+
enableJsonResponse: (bool)$env->get('MCP_ENABLE_JSON_RESPONSE', true),
290+
stateless: (bool)$env->get('MCP_STATELESS', false),
291+
);
292+
}
293+
}

src/Prompt/Extension/PromptDefinition.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
namespace Butschster\ContextGenerator\McpServer\Prompt\Extension;
66

77
use Butschster\ContextGenerator\McpServer\Prompt\PromptType;
8-
use Mcp\Types\Prompt;
9-
use Mcp\Types\PromptMessage;
8+
use PhpMcp\Schema\Content\PromptMessage;
9+
use PhpMcp\Schema\Prompt;
1010

1111
final readonly class PromptDefinition implements \JsonSerializable
1212
{

src/Prompt/Extension/TemplateResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
use Butschster\ContextGenerator\Lib\Variable\VariableResolver;
1010
use Butschster\ContextGenerator\McpServer\Prompt\Exception\TemplateResolutionException;
1111
use Butschster\ContextGenerator\McpServer\Prompt\PromptProviderInterface;
12-
use Mcp\Types\PromptMessage;
13-
use Mcp\Types\TextContent;
12+
use PhpMcp\Schema\Content\PromptMessage;
13+
use PhpMcp\Schema\Content\TextContent;
1414
use Psr\Log\LoggerInterface;
1515
use Spiral\Core\Container;
1616

src/Prompt/PromptConfigFactory.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
use Butschster\ContextGenerator\McpServer\Prompt\Exception\PromptParsingException;
1010
use Butschster\ContextGenerator\McpServer\Prompt\Extension\PromptDefinition;
1111
use Butschster\ContextGenerator\McpServer\Prompt\Extension\PromptExtension;
12-
use Mcp\Types\Prompt;
13-
use Mcp\Types\PromptArgument;
14-
use Mcp\Types\PromptMessage;
15-
use Mcp\Types\Role;
16-
use Mcp\Types\TextContent;
12+
use PhpMcp\Schema\Content\PromptMessage;
13+
use PhpMcp\Schema\Content\TextContent;
14+
use PhpMcp\Schema\Enum\Role;
15+
use PhpMcp\Schema\Prompt;
16+
use PhpMcp\Schema\PromptArgument;
1717

1818
/**
1919
* Factory for creating Prompt objects from configuration arrays.

src/Prompt/PromptMessageProcessor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
use Butschster\ContextGenerator\Lib\Variable\VariableReplacementProcessor;
99
use Butschster\ContextGenerator\Lib\Variable\VariableResolver;
1010
use Butschster\ContextGenerator\McpServer\Prompt\Extension\PromptDefinition;
11-
use Mcp\Types\PromptMessage;
12-
use Mcp\Types\TextContent;
11+
use PhpMcp\Schema\Content\PromptMessage;
12+
use PhpMcp\Schema\Content\TextContent;
1313

1414
final readonly class PromptMessageProcessor
1515
{

0 commit comments

Comments
 (0)