Skip to content

Commit 792f001

Browse files
authored
Merge pull request #94 from context-hub/issue/91
MCP improvements
2 parents 6651b97 + c8c34c1 commit 792f001

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3024
-924
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ vendor
1010
node_modules
1111
.php-cs-fixer.cache
1212
.context
13-
runtime
13+
runtime
14+
mcp-*.log

composer.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@
3131
"nikic/php-parser": "^v4.0 | ^v5.0",
3232
"vlucas/phpdotenv": "^5.6",
3333
"symfony/yaml": "^7.2",
34-
"logiscape/mcp-sdk-php": "^1.0"
34+
"logiscape/mcp-sdk-php": "^1.0",
35+
"league/route": "^6.2",
36+
"laminas/laminas-diactoros": "^3.5",
37+
"spiral/core": "^3.15"
3538
},
3639
"require-dev": {
3740
"buggregator/trap": "^1.13",

context-generator

+72-76
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,24 @@ use Butschster\ContextGenerator\Console\SelfUpdateCommand;
1515
use Butschster\ContextGenerator\Console\VersionCommand;
1616
use Butschster\ContextGenerator\Lib\Content\ContentBuilderFactory;
1717
use Butschster\ContextGenerator\Lib\Content\Renderer\MarkdownRenderer;
18+
use Butschster\ContextGenerator\Lib\Content\Renderer\RendererInterface;
1819
use Butschster\ContextGenerator\Lib\Files;
20+
use Butschster\ContextGenerator\Lib\GithubClient\GithubClientInterface;
1921
use Butschster\ContextGenerator\Lib\HttpClient\HttpClientFactory;
22+
use Butschster\ContextGenerator\Lib\HttpClient\HttpClientInterface;
23+
use Butschster\ContextGenerator\Lib\Logger\ConsoleLogger;
2024
use Butschster\ContextGenerator\Lib\Logger\LoggerFactory;
25+
use Butschster\ContextGenerator\McpServer\Registry\McpItemsRegistry;
26+
use Butschster\ContextGenerator\McpServer\Routing\McpResponseStrategy;
27+
use Butschster\ContextGenerator\McpServer\Routing\RouteRegistrar;
28+
use Butschster\ContextGenerator\Modifier\SourceModifierRegistry;
2129
use GuzzleHttp\Client;
2230
use GuzzleHttp\Psr7\HttpFactory;
31+
use League\Route\Router;
32+
use League\Route\Strategy\StrategyInterface;
33+
use Monolog\ErrorHandler;
34+
use Psr\Container\ContainerInterface;
35+
use Spiral\Core\Container;
2336
use Symfony\Component\Console\Application;
2437
use Symfony\Component\Console\Input\ArgvInput;
2538
use Symfony\Component\Console\Output\ConsoleOutput;
@@ -29,7 +42,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
2942
// Prepare Global Environment
3043
// -----------------------------------------------------------------------------
3144

32-
\error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
45+
\error_reporting(E_ERROR);
3346

3447

3548
// -----------------------------------------------------------------------------
@@ -85,6 +98,11 @@ $application->setDefaultCommand('generate');
8598
$input = new ArgvInput();
8699
$output = new SymfonyStyle($input, new ConsoleOutput());
87100

101+
$errorHandler = new ErrorHandler(new ConsoleLogger($output));
102+
$errorHandler->registerExceptionHandler();
103+
$errorHandler->registerErrorHandler();
104+
$errorHandler->registerFatalHandler();
105+
88106
$vendorPath = \dirname($vendorPath) . '/../';
89107
$versionFile = $vendorPath . '/version.json';
90108
$appPath = \realpath($vendorPath);
@@ -102,109 +120,87 @@ if ($insidePhar) {
102120
$appPath = \getcwd();
103121
}
104122

105-
// Create base services
106-
$files = new Files();
107-
$httpClient = new Client();
108-
$httpMessageFactory = new HttpFactory();
109-
$httpClientAdapter = HttpClientFactory::create(
110-
$httpClient,
111-
$httpMessageFactory,
123+
$container = new Container();
124+
$container->bindSingleton(
125+
Directories::class,
126+
new Directories(
127+
rootPath: $appPath,
128+
outputPath: $appPath . '/.context',
129+
configPath: $appPath,
130+
jsonSchemaPath: __DIR__ . '/json-schema.json',
131+
),
112132
);
113133

114-
// Create the factory chain for the GenerateCommand
115-
$contentBuilderFactory = new ContentBuilderFactory(
116-
defaultRenderer: new MarkdownRenderer(),
134+
$container->bindSingleton(ParserPluginRegistry::class, static fn() => ParserPluginRegistry::createDefault());
135+
$container->bindSingleton(ConfigurationProviderFactory::class, ConfigurationProviderFactory::class);
136+
$container->bindSingleton(FilesInterface::class, Files::class);
137+
$container->bindSingleton(StrategyInterface::class, McpResponseStrategy::class);
138+
$container->bindSingleton(
139+
SourceModifierRegistry::class,
140+
static fn(ModifierRegistryFactory $factory) => $factory->create(),
117141
);
118-
119-
$githubClientFactory = new GithubClientFactory(
120-
httpClient: $httpClientAdapter,
121-
defaultToken: \getenv('GITHUB_TOKEN') ?: null,
142+
$container->bindSingleton(
143+
HttpClientInterface::class,
144+
static function (Client $httpClient, HttpFactory $httpMessageFactory) {
145+
return HttpClientFactory::create(
146+
$httpClient,
147+
$httpMessageFactory,
148+
);
149+
},
122150
);
123-
124-
$variableResolverFactory = new VariableResolverFactory();
125-
$modifierRegistryFactory = new ModifierRegistryFactory();
126-
127-
// Create the source fetcher registry factory with its dependencies
128-
$sourceFetcherRegistryFactory = new SourceFetcherRegistryFactory(
129-
httpClient: $httpClientAdapter,
130-
contentBuilderFactory: $contentBuilderFactory,
131-
variableResolverFactory: $variableResolverFactory,
132-
githubClientFactory: $githubClientFactory,
133-
);
134-
135-
// Create the document compiler factory with its dependencies
136-
$documentCompilerFactory = new DocumentCompilerFactory(
137-
files: $files,
138-
sourceFetcherRegistryFactory: $sourceFetcherRegistryFactory,
139-
modifierRegistryFactory: $modifierRegistryFactory,
140-
contentBuilderFactory: $contentBuilderFactory,
151+
$container->bindSingleton(RendererInterface::class, MarkdownRenderer::class);
152+
$container->bindSingleton(ContentBuilderFactory::class, ContentBuilderFactory::class);
153+
$container->bindSingleton(
154+
GithubClientInterface::class,
155+
static fn(HttpClientInterface $httpClient) => new GithubClientFactory(
156+
httpClient: $httpClient,
157+
defaultToken: \getenv('GITHUB_TOKEN') ?: null,
158+
),
141159
);
142160

143-
$configProviderFactory = new ConfigurationProviderFactory(
144-
parserPluginRegistry: ParserPluginRegistry::createDefault(),
145-
);
161+
$container->bindSingleton(LoggerFactory::class, LoggerFactory::class);
162+
$container->bindSingleton(Router::class, static function (StrategyInterface $strategy, ContainerInterface $container) {
163+
$router = new Router();
164+
$strategy->setContainer($container);
165+
$router->setStrategy($strategy);
146166

147-
$logger = LoggerFactory::create(
148-
output: $output,
149-
loggingEnabled: $output->isVerbose() || $output->isDebug() || $output->isVeryVerbose(),
150-
);
167+
return $router;
168+
});
169+
$container->bindSingleton(RouteRegistrar::class, RouteRegistrar::class);
170+
$container->bindSingleton(McpItemsRegistry::class, McpItemsRegistry::class);
151171

152172
// Register all commands
153173
$application->add(
154-
new VersionCommand(
155-
version: $version['version'] ?? 'dev',
156-
httpClient: $httpClientAdapter,
157-
),
174+
$container->make(VersionCommand::class, [
175+
'version' => $version['version'] ?? 'dev',
176+
]),
158177
);
159178

160179
$application->add(
161-
new InitCommand(
162-
baseDir: $appPath,
163-
files: $files,
164-
),
180+
$container->make(InitCommand::class),
165181
);
166182

167183
$application->add(
168-
new SchemaCommand(
169-
httpClient: $httpClientAdapter,
170-
),
184+
$container->make(SchemaCommand::class),
171185
);
172186

173187
$application->add(
174-
new SelfUpdateCommand(
175-
version: $version['version'] ?? 'dev',
176-
httpClient: $httpClientAdapter,
177-
files: $files,
178-
binaryType: $type,
179-
),
188+
$container->make(SelfUpdateCommand::class, [
189+
'version' => $version['version'] ?? 'dev',
190+
'binaryType' => $type,
191+
]),
180192
);
181193

182194
$application->add(
183-
new GenerateCommand(
184-
rootPath: $appPath,
185-
outputPath: $appPath . '/.context',
186-
files: $files,
187-
documentCompilerFactory: $documentCompilerFactory,
188-
configurationProviderFactory: $configProviderFactory,
189-
),
195+
$container->make(GenerateCommand::class),
190196
);
191197

192198
$application->add(
193-
new DisplayCommand(
194-
rootPath: $appPath,
195-
files: $files,
196-
configurationProviderFactory: $configProviderFactory,
197-
),
199+
$container->make(DisplayCommand::class),
198200
);
199201

200202
$application->add(
201-
new MCPServerCommand(
202-
rootPath: $appPath,
203-
jsonSchemaPath: __DIR__ . '/json-schema.json',
204-
files: $files,
205-
documentCompilerFactory: $documentCompilerFactory,
206-
configurationProviderFactory: $configProviderFactory,
207-
),
203+
$container->make(MCPServerCommand::class),
208204
);
209205

210206
$application->run($input, $output);

context.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,22 @@ documents:
252252
sourcePaths:
253253
- tests
254254

255+
- description: "MCP Server Actions"
256+
outputPath: "mcp/actions.md"
257+
sources:
258+
- type: file
259+
sourcePaths:
260+
- src/McpServer/Action
261+
262+
- description: "MCP Server routing"
263+
outputPath: "mcp/routing.md"
264+
sources:
265+
- type: file
266+
sourcePaths:
267+
- src/McpServer/Routing
268+
- src/McpServer/Server.php
269+
- src/McpServer/ServerFactory.php
270+
255271
- description: "Changes in the Project"
256272
outputPath: "changes.md"
257273
sources:

src/ConfigLoader/context.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
documents:
2+
- description: Configuration System
3+
outputPath: core/config-loader.md
4+
sources:
5+
- type: file
6+
sourcePaths: .
7+
filePattern: '*.php'
8+
showTreeView: true

src/ConfigurationProviderConfig.php

+3-14
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,13 @@
44

55
namespace Butschster\ContextGenerator;
66

7-
use Butschster\ContextGenerator\ConfigLoader\Parser\ConfigParserPluginInterface;
8-
use Butschster\ContextGenerator\Lib\Logger\HasPrefixLoggerInterface;
9-
use Psr\Log\LoggerInterface;
10-
7+
/**
8+
* @deprecated
9+
*/
1110
final readonly class ConfigurationProviderConfig
1211
{
13-
/**
14-
* @param string $rootPath The root path for configuration files
15-
* @param FilesInterface $files File system interface
16-
* @param string|null $configPath Specific configuration file path
17-
* @param string|null $inlineConfig Inline JSON configuration
18-
* @param HasPrefixLoggerInterface&LoggerInterface $logger Logger instance
19-
* @param array<ConfigParserPluginInterface> $parserPlugins Parser plugins for configuration
20-
*/
2112
public function __construct(
2213
public string $rootPath,
23-
public FilesInterface $files,
24-
public LoggerInterface $logger,
2514
public ?string $configPath = null,
2615
public ?string $inlineConfig = null,
2716
public array $parserPlugins = [],

src/ConfigurationProviderFactory.php

+11-14
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,28 @@
77
use Butschster\ContextGenerator\ConfigLoader\ConfigLoaderFactory;
88
use Butschster\ContextGenerator\ConfigLoader\ConfigurationProvider;
99
use Butschster\ContextGenerator\ConfigLoader\Parser\ParserPluginRegistry;
10+
use Butschster\ContextGenerator\Lib\Logger\HasPrefixLoggerInterface;
1011

1112
final readonly class ConfigurationProviderFactory
1213
{
1314
public function __construct(
1415
private ParserPluginRegistry $parserPluginRegistry,
16+
private FilesInterface $files,
17+
private HasPrefixLoggerInterface $logger,
1518
) {}
1619

17-
public function create(ConfigurationProviderConfig $config): ConfigurationProvider
20+
public function create(Directories $dirs): ConfigurationProvider
1821
{
19-
// If custom parser plugins are not provided, use the ones from the registry
20-
$parserPlugins = $config->parserPlugins;
21-
if (empty($parserPlugins)) {
22-
$parserPlugins = $this->parserPluginRegistry->getPlugins();
23-
}
24-
2522
return new ConfigurationProvider(
2623
loaderFactory: new ConfigLoaderFactory(
27-
files: $config->files,
28-
rootPath: $config->rootPath,
29-
logger: $config->logger->withPrefix('config-loader'),
24+
files: $this->files,
25+
rootPath: $dirs->rootPath,
26+
logger: $this->logger->withPrefix('config-loader'),
3027
),
31-
files: $config->files,
32-
rootPath: $config->rootPath,
33-
logger: $config->logger->withPrefix('config-provider'),
34-
parserPlugins: $parserPlugins,
28+
files: $this->files,
29+
rootPath: $dirs->rootPath,
30+
logger: $this->logger->withPrefix('config-provider'),
31+
parserPlugins: $this->parserPluginRegistry->getPlugins(),
3532
);
3633
}
3734
}

0 commit comments

Comments
 (0)