Skip to main content
Glama
weijianzhg

mCP 2.0

by weijianzhg

무상태 MCP, 상태 기반 애플리케이션

MCP TypeScript SDK v2를 사용하여 MCP 2026-07-28의 주요 요청/응답 패턴을 보여주는 최소 JavaScript 프로젝트입니다.

데모가 다루는 내용:

  • Mcp-Session-Id가 없는 무상태 HTTP 요청.

  • 명시적 핸들로 주소를 지정하는 애플리케이션 관리 상태.

  • 사용자 확인을 위한 다중 왕복 요청.

  • 개별 작업 범위의 진행 상황 업데이트.

  • TTL 및 공유 범위를 갖춘 캐시 가능한 도구 검색.

실행

Node.js 20 이상이 필요합니다.

npm install
npm run demo

이 명령은 사용 가능한 로컬 포트에서 서버를 시작하고, 모든 시나리오를 실행하며, 결과를 출력한 후 서버를 종료합니다. 삭제 예제는 인메모리 가상 파일 세트를 사용하며 디스크의 파일을 건드리지 않습니다.

통합 테스트는 다음으로 실행합니다:

npm test

다른 MCP 클라이언트를 위해 서버를 계속 실행하려면:

npm run server

엔드포인트는 http://127.0.0.1:3000/mcp입니다. PORT를 설정하여 포트를 변경할 수 있습니다.

Related MCP server: MCP RC Long-Running Task Prototype

1. 무상태 요청

MCP 2026-07-28은 프로토콜 수준의 HTTP 세션을 제거합니다. 이 프로젝트는 각 요청마다 새 McpServer를 생성하므로, 어떤 요청이든 모든 서버 인스턴스에 도달할 수 있습니다:

const mcpHandler = createMcpHandler(
  () => createStateServer(demoFiles),
  {
    legacy: "reject",
    onerror: (error) => console.error("MCP error:", error),
  },
);

클라이언트는 최신 프로토콜 버전을 명시적으로 선택합니다:

const client = new Client(
  { name: "state-demo-client", version: "0.1.0" },
  {
    capabilities: { elicitation: { form: {} } },
    versionNegotiation: {
      mode: { pin: "2026-07-28" },
    },
  },
);

따라서 하나의 McpServer 인스턴스 내부에 저장된 상태는 해당 요청 후 사라집니다. 데모의 임시 카운터를 두 번 호출하면 두 개의 서로 다른 서버 인스턴스에서 1이 생성됩니다.

2. 상태 기반 애플리케이션 데이터

무상태 MCP가 무상태 애플리케이션을 요구하지는 않습니다. 영구 상태는 요청별 MCP 서버 외부에 있어야 하며 명시적 핸들을 사용하여 선택됩니다:

const countersById = new Map();

server.registerTool(
  "create-counter",
  {
    outputSchema: z.object({ counterId: z.uuid(), value: z.number() }),
  },
  async () => {
    const counterId = randomUUID();
    countersById.set(counterId, 0);
    const structuredContent = { counterId, value: 0 };

    return {
      content: [{ type: "text", text: JSON.stringify(structuredContent) }],
      structuredContent,
    };
  },
);

클라이언트는 그 외에는 독립적인 호출 사이에서 핸들을 전달합니다:

const created = await client.callTool({ name: "create-counter" });
const { counterId } = created.structuredContent;

await client.callTool({
  name: "increment-counter",
  arguments: { counterId },
});

인메모리 Map은 단지 임시 대체일 뿐입니다. 프로덕션 서버는 데이터베이스나 공유 저장소를 사용하고, 핸들을 인증된 주체에 바인딩하며, 모든 조회에서 권한 부여와 만료를 강제해야 합니다.

3. 다중 왕복 확인

추가 입력이 필요한 도구는 input_required를 반환합니다. 클라이언트는 답변을 얻은 후 inputResponses로 원래 요청을 재시도하므로, 서버는 작업 스트림에서 두 번째 JSON-RPC 요청을 시작하지 않습니다.

server.registerTool(
  "delete-files",
  {
    inputSchema: z.object({ files: z.array(z.string()).min(1) }),
    annotations: { destructiveHint: true },
  },
  async ({ files }, ctx) => {
    const confirmation = acceptedContent(
      ctx.mcpReq.inputResponses,
      "confirm",
      confirmationSchema,
    );

    if (confirmation === undefined) {
      return inputRequired({
        inputRequests: {
          confirm: inputRequired.elicit({
            message: `Delete ${files.length} virtual files?`,
            requestedSchema: confirmationSchema,
          }),
        },
      });
    }

    if (!confirmation.confirm) {
      return {
        content: [{ type: "text", text: "Cancelled" }],
        structuredContent: { status: "cancelled", deleted: [] },
      };
    }

    const deleted = files.filter((file) => demoFiles.delete(file));
    return {
      content: [{ type: "text", text: `Deleted: ${deleted.join(", ")}` }],
      structuredContent: { status: "deleted", deleted },
    };
  },
);

SDK는 일반 elicitation 핸들러를 사용하여 요청을 처리하고 자동으로 재시도할 수 있습니다:

client.setRequestHandler("elicitation/create", async (request) => {
  const confirm = await askUser(request.params.message);
  return {
    action: "accept",
    content: { confirm },
  };
});

확인은 사용자 경험이지 권한 부여가 아닙니다. 서버는 여전히 호출자를 인증하고 파일 삭제 권한을 독립적으로 강제해야 합니다.

4. 요청 범위 진행 상황

진행 상황은 작업을 시작한 요청에 계속 연결됩니다. 동시 작업은 하나의 전역 이벤트 스트림을 공유하는 대신 자신의 업데이트만 받습니다.

server.registerTool(
  "run-work",
  { inputSchema: z.object({ job: z.string() }) },
  async ({ job }, ctx) => {
    const progressToken = ctx.mcpReq._meta?.progressToken;

    for (const progress of [10, 30, 70]) {
      if (progressToken !== undefined) {
        await ctx.mcpReq.notify({
          method: "notifications/progress",
          params: {
            progressToken,
            progress,
            total: 100,
            message: `${job}: ${progress}%`,
          },
        });
      }
    }

    return {
      content: [{ type: "text", text: `${job}: complete` }],
      structuredContent: { job, status: "complete" },
    };
  },
);

각 클라이언트 호출은 자체 진행 콜백을 제공합니다:

await Promise.all([
  client.callTool(
    { name: "run-work", arguments: { job: "alpha" } },
    { onprogress: (update) => alphaProgress.push(update) },
  ),
  client.callTool(
    { name: "run-work", arguments: { job: "beta" } },
    { onprogress: (update) => betaProgress.push(update) },
  ),
]);

5. 캐시 가능성

캐시 가능한 응답에는 신선도 수명과 공유 정책이 포함됩니다. 이는 하나의 에이전트가 여러 MCP 서버에 연결할 때 반복적인 검색 트래픽을 줄여줍니다.

이 서버는 도구 카탈로그를 5분 동안 재사용 가능으로 표시합니다:

const server = new McpServer(
  { name: "mcp-state-demo", version: "0.1.0" },
  {
    cacheHints: {
      "tools/list": {
        ttlMs: 300_000,
        cacheScope: "public",
      },
    },
  },
);

클라이언트는 최신 항목을 자동으로 사용합니다:

await client.listTools(); // network request; stores the result
await client.listTools(); // cache hit; no network request

await client.listTools(undefined, {
  cacheMode: "refresh",
}); // forces a network request and updates the cache

이 필드는 tools/list, prompts/list, resources/list, resources/templates/list, resources/read 결과에 적용됩니다.

  • public은 클라이언트와 공유 중간 계층이 사용자 간에 결과를 재사용할 수 있게 합니다.

  • private은 재사용을 요청한 인증 컨텍스트로 제한합니다. 캐시 저장소가 공유되는 경우 클라이언트의 cachePartition을 안정적인 주체 식별자로 설정하세요.

TTL은 신선도 추정치입니다. 목록 변경 알림은 TTL이 만료되기 전에 캐시된 카탈로그를 무효화할 수 있습니다.

프로젝트 구조

src/server.js       MCP server and tool implementations
src/demo.js         Client exercising all five scenarios
test/state.test.js  Integration tests for every scenario

MCP 패키지는 2.0.0으로 고정되어 있으며, 여기서 사용된 inputRequired, acceptedContent, 캐싱, 요청 범위 진행 상황 API를 포함합니다.

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A reference implementation demonstrating proper MCP server patterns with HTTP transport, featuring session management, progress notifications, and example tools for testing server functionality. Serves as a clean template for building MCP servers with streamable responses and comprehensive error handling.
    7
  • F
    license
    Not graded
    quality
    C
    maintenance
    Demonstrates MCP 2026-07-28 behavior for long-running tool calls, task lifecycle (get, update, cancel), and elicitation clarification during async tasks.
  • A
    license
    Not graded
    quality
    C
    maintenance
    Educational MCP server demonstrating the 2026-07-28 stateless protocol with raw Starlette, no SDK, featuring tools, request state handles, MRTR elicitation, and subscriptions.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Demo MCP server for ACEL, a runtime verification middleware that blocks a rule-violating tool call before it executes. 5 tools (authenticate, read/validate/delete records, send payment) showing ACEL enforcing call ordering and state preconditions live via the official MCP SDK's middleware hook.
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/weijianzhg/mcp-2-0'

If you have feedback or need assistance with the MCP directory API, please join our Discord server