PHP SDK 첫 서버 만들기
PHP SDK 첫 서버 만들기
MCP 서버는 사실 서너 줄의 연결 코드와 함께 있는 평범한 PHP 클래스일 뿐이에요. vendor/ 디렉토리 옆에 server.php를 만들면 첫 서버가 완성됩니다.
server.php
#!/usr/bin/env php
<?php
require __DIR__.'/vendor/autoload.php';
use Mcp\Capability\Attribute\McpResource;
use Mcp\Capability\Attribute\McpTool;
use Mcp\Server;
use Mcp\Server\Transport\StdioTransport;
class Calculator
{
/**
* Adds two numbers.
*/
#[McpTool]
public function add(int $a, int $b): int
{
return $a + $b;
}
#[McpResource(uri: 'config://calculator/settings')]
public function settings(): array
{
return ['precision' => 2];
}
}
exit(Server::builder()
->setServerInfo('Calculator', '1.0.0')
->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor'])
->build()
->run(new StdioTransport()));
탐색은 symfony/finder가 필요해요:
composer require symfony/finder
각 부분이 하는 일
#[McpTool]은 메서드를 모델이 호출할 수 있는 액션으로 표시해요. 이름은 메서드 이름에서, 설명은 docblock(요약 + 더 긴 설명을 쓰면 그것까지)에서, 입력 스키마는 파라미터 타입에서 생성돼요 — int $a, int $b는 필수 정수 두 개를 가진 JSON Schema가 됩니다. Tools 참고
#[McpResource]은 메서드를 애플리케이션이 URI로 읽을 수 있는 읽기 전용 데이터로 표시해요. Resources 참고
setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor'])는 그 디렉토리들에서 속성이 붙은 클래스를 스캔해요. 스캔은 lazy해서 첫 요청이 레지스트리를 필요로 할 때( build() 반환 시점이 아니라) 발생합니다 — 미리 비용을 지불하고 싶다면 setLazyLoading(false)를 호출하세요. vendor를 제외하는 게 중요한 이유는 스캔이 재귀적이라, 그대로 두면 의존성이 실어 나르는 모든 파일을 읽고 오토로드하기 때문이에요. 요소를 명시적으로 등록하고 싶다면(또는 둘을 섞으려면) Registering elements를 참고하세요.
run(new StdioTransport())는 stdin/stdout 위에서 JSON-RPC를 주고받고 exit 코드를 반환해요. 이는 로컬 MCP 호스트가 서브프로세스로 실행하는 트랜스포트이며, 웹에 노출할 서버라면 대신 HTTP 트랜스포트를 쓰세요.
절대 STDOUT에 쓰지 마세요 — STDIO 트랜스포트에서는
STDOUT이 곧 프로토콜이에요. 핸들러에서echo,print_r(), 어중간한var_dump()가 스트림을 깨뜨립니다.STDERR에 쓰거나 logger를 사용하세요.
실행하기
php server.php
아무 일도 일어나지 않아요 — 서버가 stdin에서 JSON-RPC를 기다리는 거라서, 정확히 맞는 동작이에요. Ctrl+C로 멈추고, 진짜 클라이언트가 대신 실행하게 하세요: Inspector로 시험해 보기.
더 알아보기 (Learn more)
- PHP SDK 개요 — SDK 소개와 다음 단계
- Inspector로 시험 — UI에서 클릭해보기