Lewati ke konten
getnextpdf.com

Jalankan sesi dokumen agen melalui MCP

Ini adalah satu sesi agen lengkap dengan server Model Context Protocol (MCP) NextPDF Connect, pesan demi pesan: initialize, tools/list, enam pemanggilan tools/call yang membangun sebuah brief proyek satu halaman, dan perjalanan bolak-balik human-in-the-loop (HITL) yang menerapkan gerbang pada penulisan berkas akhir. Setiap pesan JSON-RPC di bawah direkam apa adanya dari proses bin/nextpdf-mcp yang aktif (hanya tool tingkatan core), lalu disanitasi dengan tepat dua cara: token konfirmasi sekali-pakai ditampilkan sebagai confirm_<single-use-hex>, dan direktori sementara sistem pada mesin dipersingkat menjadi C:\Temp. Identifier, skema, posisi, dan jumlah byte persis seperti yang dikirim server.

Terminal window
composer require nextpdf/server

Kaitkan transport stdio di host MCP Anda — untuk Claude Desktop (host meluncurkan perintah dari direktorinya sendiri, jadi gunakan path absolut; transport stdio tidak memerlukan API key, tidak seperti transport REST):

{
"mcpServers": {
"nextpdf": {
"command": "php",
"args": ["/absolute/path/to/your/project/vendor/bin/nextpdf-mcp"]
}
}
}

Server berbicara JSON-RPC 2.0 yang dipisahkan baris-baru pada stdin/stdout dan menjaga output protokol tetap terpisah secara tegas dari diagnostik: baris startup dan audit diarahkan ke stderr, tidak pernah stdout.

Sesi dokumen MCP bersifat stateful. create_pdf membuka sebuah dokumen di penyimpanan dalam-memori server dan mengembalikan sebuah document_id; setiap panggilan berikutnya menargetkan identifier tersebut. Tool konten (set_font, add_text, add_table) dieksekusi seketika pada tingkat risiko Caution dengan pencatatan audit; preview_layout adalah pembacaan Safe; dan output_pdf dengan sebuah file_path bersifat Approval Required — ia tidak berjalan pada panggilan pertama. Sebaliknya, server mengembalikan sebuah tantangan dengan token sekali-pakai, agen meneruskan tantangan tersebut ke manusia, dan hanya panggilan-ulang yang membawa _confirmation_token yang mengeksekusi penulisan. Dokumen yang tertinggal di penyimpanan kedaluwarsa setelah masa-hidup yang dikonfigurasi (30 menit secara bawaan).

Pemanggilan tool yang sama menggerakkan mesin tool melalui REST dan gRPC — semua transport ini berbagi satu executor — sehingga semua yang ada di sini kecuali framing stdio ikut berlaku. Lihat Render faktur secara menyeluruh melalui REST untuk mesin yang sama pada permukaan HTTP.

ToolPeran dalam sesi iniTingkat risiko
create_pdfMembuka dokumen, dapatkan document_idCaution
set_fontPilih heading, lalu muka huruf isiCaution
add_textBaris judul, lalu paragraf pengantarCaution
add_tableTabel checklist pemilik/tanggal-jatuh-tempoCaution
preview_layoutBaca keadaan tata letak sebelum outputSafe
output_pdf (mode berkas)Tulis PDF — digerbangiApproval Required

Deployment yang direkam di sini mendaftarkan 20 tool (13 core, 6 Pro, 1 Enterprise — jumlahnya muncul di respons initialize di bawah); sesi ini hanya menggunakan tool core, sehingga berjalan tanpa perubahan pada instalasi yang hanya open-source. Katalog rujukan adalah balasan tools/list dari server Anda sendiri, dan tangga risiko didefinisikan dalam rujukan tingkatan risiko HITL.

Klien membuka sesi dan menyatakan versi protokolnya:

{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "planning-agent",
"version": "1.0.0"
}
}
}

Server mengonfirmasi versi protokol dan mendeklarasikan kapabilitasnya, termasuk jumlah tool per tingkatan dan bahwa penggerbangan HITL diaktifkan:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": false
},
"nextpdf": {
"tiers": {
"core": 13,
"pro": 6,
"enterprise": 1
},
"tool_count": 20,
"risk_model_version": 1,
"hitl_enabled": true
}
},
"serverInfo": {
"name": "NextPDF Connect",
"version": "1.0.0"
}
}
}

Klien memberikan pengakuan dengan sebuah notifikasi (notifikasi tidak membawa id dan tidak menerima respons):

{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}

Balasan lengkap mencantumkan seluruh 20 tool yang terdaftar beserta skema input lengkapnya. Yang ditampilkan di sini dipersingkat menjadi dua tool yang membuka dan menutup sesi ini — 18 entri yang dihilangkan memiliki bentuk yang sama:

{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "create_pdf",
"description": "Create a new PDF document and return a document_id for subsequent operations",
"inputSchema": {
"type": "object",
"properties": {
"page_size": {
"type": "string",
"description": "Page size name (e.g. \"A4\", \"Letter\", \"Legal\", \"A3\")",
"default": "A4"
},
"orientation": {
"type": "string",
"enum": [
"portrait",
"landscape"
],
"description": "Page orientation",
"default": "portrait"
},
"title": {
"type": "string",
"description": "Document title metadata"
},
"author": {
"type": "string",
"description": "Document author metadata"
}
},
"required": []
},
"annotations": {
"destructiveHint": false,
"idempotentHint": false
}
},
{
"name": "output_pdf",
"description": "Finalize the PDF and output to file or return as base64",
"inputSchema": {
"type": "object",
"properties": {
"document_id": {
"type": "string",
"description": "The document_id returned by create_pdf"
},
"file_path": {
"type": "string",
"description": "Absolute file path to save the PDF. If omitted, returns base64-encoded PDF data."
},
"destroy": {
"type": "boolean",
"description": "Whether to remove the document from the store after output",
"default": true
}
},
"required": [
"document_id"
]
},
"annotations": {
"destructiveHint": false,
"openWorldHint": true
}
}
]
}
}

Perhatikan skema output_pdf: file_path bersifat opsional, dan anotasinya membawa openWorldHint: true — tool ini dapat menyentuh dunia di luar sesi, justru itulah sebabnya mode berkas digerbangi.

{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "create_pdf",
"arguments": {
"page_size": "A4",
"orientation": "portrait",
"title": "Project kickoff brief",
"author": "Planning agent"
}
}
}
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"page_count\":1,\"page_size\":\"A4\",\"orientation\":\"portrait\"}"
}
],
"structuredContent": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"page_count": 1,
"page_size": "A4",
"orientation": "portrait"
}
}
}

Setiap hasil tool tiba dua kali dalam satu pesan: sebuah blok teks content yang dapat dibaca manusia, dan structuredContent yang dapat dibaca mesin. Baca structuredContent.document_id dan teruskan melalui setiap panggilan berikutnya.

Atur muka huruf tebal 16-poin, lalu tempatkan judul:

{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "set_font",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"family": "helvetica",
"style": "B",
"size": 16
}
}
}
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"content": [
{
"type": "text",
"text": "Font set to helvetica B 16pt on document doc_3b9f435efa0f32d1da7a131d."
}
]
}
}
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "add_text",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"text": "Project kickoff brief"
}
}
}
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"content": [
{
"type": "text",
"text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"position\":{\"x\":10,\"y\":16,\"page\":0}}"
}
],
"structuredContent": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"position": {
"x": 10,
"y": 16,
"page": 0
}
}
}
}

Kembali ke muka huruf reguler 11-poin untuk teks pengantar; width: 0 memilih tata letak multi-sel lebar-penuh:

{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "set_font",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"family": "helvetica",
"style": "",
"size": 11
}
}
}
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"content": [
{
"type": "text",
"text": "Font set to helvetica 11pt on document doc_3b9f435efa0f32d1da7a131d."
}
]
}
}
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "add_text",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"text": "Prepared by the planning agent for the 14 July kickoff. Scope, owners, and the first-week checklist are tabled below.",
"width": 0,
"line_height": 6
}
}
}
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [
{
"type": "text",
"text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"position\":{\"x\":10,\"y\":29.75,\"page\":0}}"
}
],
"structuredContent": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"position": {
"x": 10,
"y": 29.75,
"page": 0
}
}
}
}
{
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "add_table",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"html": "<table><tr><th>Work item</th><th>Owner</th><th>Due</th></tr><tr><td>Repository bootstrap</td><td>Devon</td><td>2026-07-15</td></tr><tr><td>CI pipeline</td><td>Ana</td><td>2026-07-17</td></tr><tr><td>Staging deploy</td><td>Priya</td><td>2026-07-21</td></tr></table>"
}
}
}
{
"jsonrpc": "2.0",
"id": 8,
"result": {
"content": [
{
"type": "text",
"text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"position\":{\"x\":10,\"y\":84.75,\"page\":0}}"
}
],
"structuredContent": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"position": {
"x": 10,
"y": 84.75,
"page": 0
}
}
}
}

Setiap panggilan konten mengembalikan position kursor yang diperbarui, sehingga agen selalu tahu di mana elemen berikutnya akan mendarat.

preview_layout adalah panggilan Safe yang hanya-baca — agen yang berperilaku baik memeriksa apa yang telah dibangunnya sebelum meminta manusia menyetujui sebuah penulisan:

{
"jsonrpc": "2.0",
"id": 9,
"method": "tools/call",
"params": {
"name": "preview_layout",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d"
}
}
}
{
"jsonrpc": "2.0",
"id": 9,
"result": {
"content": [
{
"type": "text",
"text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"total_pages\":1,\"current_page\":0,\"page_dimensions\":{\"width\":595.276,\"height\":841.89},\"margins\":{\"top\":10,\"right\":10,\"bottom\":10,\"left\":10},\"cursor_position\":{\"x\":10,\"y\":84.75}}"
}
],
"structuredContent": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"total_pages": 1,
"current_page": 0,
"page_dimensions": {
"width": 595.276,
"height": 841.89
},
"margins": {
"top": 10,
"right": 10,
"bottom": 10,
"left": 10
},
"cursor_position": {
"x": 10,
"y": 84.75
}
}
}
}

8. Minta penulisan berkas — gerbang menjawab lebih dulu

Bagian berjudul “8. Minta penulisan berkas — gerbang menjawab lebih dulu”

Agen meminta output_pdf untuk menulis brief yang telah selesai ke disk, menjaga dokumen tetap hidup (destroy: false) untuk berjaga-jaga jika manusia menolak dan ia perlu beralih ke output base64:

{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "output_pdf",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"file_path": "C:\\Temp\\nextpdf-mcp\\kickoff-brief.pdf",
"destroy": false
}
}
}

Berkas tidak ditulis. Karena mode berkas bersifat Approval Required, server justru menjawab dengan sebuah tantangan konfirmasi:

{
"jsonrpc": "2.0",
"id": 10,
"result": {
"content": [
{
"type": "text",
"text": "⚠️ CONFIRMATION REQUIRED\n\nOperation: output_pdf\nDescription: Finalize the PDF and output to file or return as base64\n\nTo proceed, call output_pdf again with parameter _confirmation_token: \"confirm_<single-use-hex>\"\nExpires in 300 seconds."
}
],
"isError": false
}
}

9. Manusia menyetujui — panggil ulang dengan token

Bagian berjudul “9. Manusia menyetujui — panggil ulang dengan token”

Agen meneruskan teks tantangan ke manusia. Setelah disetujui, ia memanggil output_pdf lagi dengan argumen yang sama ditambah _confirmation_token:

{
"jsonrpc": "2.0",
"id": 11,
"method": "tools/call",
"params": {
"name": "output_pdf",
"arguments": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"file_path": "C:\\Temp\\nextpdf-mcp\\kickoff-brief.pdf",
"destroy": false,
"_confirmation_token": "confirm_<single-use-hex>"
}
}
}

Token dikonsumsi, penulisan dieksekusi, dan hasilnya melaporkan berkas yang ditulis:

{
"jsonrpc": "2.0",
"id": 11,
"result": {
"content": [
{
"type": "text",
"text": "{\"document_id\":\"doc_3b9f435efa0f32d1da7a131d\",\"file_path\":\"C:\\\\Temp\\\\nextpdf-mcp\\\\kickoff-brief.pdf\",\"file_size\":3612,\"page_count\":1,\"destroyed\":false}"
}
],
"structuredContent": {
"document_id": "doc_3b9f435efa0f32d1da7a131d",
"file_path": "C:\\Temp\\nextpdf-mcp\\kickoff-brief.pdf",
"file_size": 3612,
"page_count": 1,
"destroyed": false
}
}
}

Sesi ini menulis kickoff-brief.pdf (3.612 byte, satu halaman, cocok dengan structuredContent.file_size dan page_count). Output qpdf --check yang direkam untuk berkas persis itu:

checking kickoff-brief.pdf
PDF Version: 2.0
File is not encrypted
File is not linearized
No syntax or stream encoding errors found; the file may still contain
errors that qpdf cannot detect

Itu adalah pemeriksaan struktural, dalam kata-kata qpdf sendiri — bukan penetapan konformansi.

  • Panggilan-ulang harus mengulang argumen yang sama. Token konfirmasi terikat pada nama tool ditambah digest kanonis dari argumen yang untuknya token itu diterbitkan. Memanggil ulang dengan sesuatu yang berubah — bahkan sekadar membalik destroy — tidak mengonsumsi token; server justru menjawab dengan tantangan baru. Ulangi argumen yang persis sama dan hanya tambahkan _confirmation_token.
  • Token bersifat sekali-pakai dan kedaluwarsa. Tantangan menyatakan kedaluwarsanya (300 detik). Setelah kedaluwarsa atau dikonsumsi, panggilan tergerbang berikutnya mendapat tantangan baru; teruskan yang baru itu.
  • Output berkas mendarat di dalam direktori yang masuk daftar-izin. Server menolak sebuah file_path di luar direktori sementaranya yang dikonfigurasi dengan Output path rejected by security policy. Akar daftar-izin bawaan adalah nextpdf-mcp di bawah direktori sementara sistem; operator mengubahnya dengan pengaturan temp_dir di nextpdf-mcp.yaml.
  • Mode base64 tidak digerbangi. output_pdf tanpa file_path mengembalikan PDF sebagai base64 pada tingkat Review, tanpa efek samping sistem berkas — lihat Wajibkan persetujuan manusia untuk output berkas untuk pembahasan mendalam tentang batas itu.
  • Sebuah tantangan adalah hasil, bukan kesalahan. Pesan tantangan tiba dengan isError: false; persetujuan yang tertunda adalah jeda alur kerja. Jangan mencoba ulang dalam sebuah loop, dan jangan pernah memalsukan token.
  • Notifikasi tidak mendapat balasan. Setelah notifications/initialized, jangan memblokir untuk menunggu baris respons.

Sesi ini bersifat dalam-memori dari ujung ke ujung: panggilan konten kembali dalam milidetik pada jalannya yang direkam, dan waktu yang berlalu didominasi oleh perjalanan bolak-balik persetujuan manusia, yang justru merupakan inti dari gerbang. Penyimpanan dokumen menahan sebuah sesi selama 30 menit waktu diam secara bawaan (maksimum 50 dokumen), sehingga persetujuan yang lambat tidak kehilangan dokumen yang telah dibangun — tetapi dokumen yang ditinggalkan akan dibersihkan.

  • Perlakukan token konfirmasi sebagai rahasia sekali-pakai. Teruskan teks tantangan ke manusia; jangan mencatat token atau menyimpannya secara persisten. Halaman ini menyunting token yang direkam justru karena alasan itu.
  • Jejak audit berada di stderr. Setiap eksekusi pada tingkat Caution atau lebih tinggi dicatat sebagai audit (tool, risiko, argumen, hasil) melalui PSR-3, dengan parameter sensitif disunting. Diagnostik tidak pernah bercampur ke dalam aliran protokol.
  • Daftar-izin path adalah batas sistem berkas. Arahkan temp_dir ke sebuah direktori yang didedikasikan untuk output Connect; jangan memperlebarnya ke lokasi serba-guna.
  • Tingkat risiko hanya bergerak naik. Sebuah override operator di nextpdf-mcp.yaml dapat menaikkan tingkat risiko sebuah tool tetapi tidak pernah dapat menurunkan output_pdf di bawah Approval Required.

Resep ini tidak membuat klaim standar normatif apa pun. Ia mendokumentasikan transport stdio MCP (JSON-RPC 2.0, versi protokol 2025-06-18 sebagaimana dinegosiasikan dalam pertukaran initialize yang direkam) dan kontrak risiko serta konfirmasi milik server. Langkah qpdf --check di atas hanya mengonfirmasi integritas struktural berkas yang ditulis; konformansi terhadap sebuah standar ditetapkan oleh validator independen, bukan ditegaskan oleh perangkat lunak yang memproduksinya.