@@ -20,6 +20,7 @@ SAPI_BASE_PATH=${SAPI_BASE_PATH:-api}
2020SAPI_VERSION=${SAPI_VERSION:- v1}
2121SERVER_LOG=${SERVER_LOG:-/ tmp/ emcp-demo-php-server.log}
2222SERVER_PID=
23+ TMP_DIR=
2324
2425if [ " ${# SAPI_JWT_SECRET} " -lt 32 ]; then
2526 echo " [demo-verify] SAPI_JWT_SECRET is shorter than 32 chars; using SHA-256 normalized secret for compatibility."
@@ -42,6 +43,9 @@ case "${DEMO_CORE_DIR}" in
4243 * ) DEMO_CORE_DIR_PATH=" ${REPO_ROOT} /${DEMO_CORE_DIR} " ;;
4344esac
4445
46+ LOGS_MD=${LOGS_MD:- ${DEMO_DIR_PATH} / logs.md}
47+ TMP_DIR=$( mktemp -d " ${TMPDIR:-/ tmp} /emcp-demo-verify.XXXXXX" )
48+
4549cleanup () {
4650 status=$?
4751
@@ -50,6 +54,10 @@ cleanup() {
5054 wait " ${SERVER_PID} " 2> /dev/null || true
5155 fi
5256
57+ if [ -n " ${TMP_DIR} " ] && [ -d " ${TMP_DIR} " ]; then
58+ rm -rf " ${TMP_DIR} " > /dev/null 2>&1 || true
59+ fi
60+
5361 if [ " ${status} " -ne 0 ] && [ -f " ${SERVER_LOG} " ]; then
5462 echo " [demo-verify] FAILED (exit ${status} ). php -S log tail:" >&2
5563 tail -n 120 " ${SERVER_LOG} " >&2 || true
@@ -170,13 +178,230 @@ echo $signingInput . "." . $base64Url($signature);
170178' )
171179fi
172180
181+ token_masked=$( printf ' %s' " ${token} " | php -r '
182+ $token = trim((string)stream_get_contents(STDIN));
183+ if ($token === "") {
184+ echo "";
185+ exit(0);
186+ }
187+ $len = strlen($token);
188+ if ($len <= 24) {
189+ echo $token;
190+ exit(0);
191+ }
192+ echo substr($token, 0, 16) . "..." . substr($token, -8);
193+ ' )
194+
195+ mcp_url=" ${DEMO_BASE_URL}${api_prefix} /mcp/${EMCP_SERVER_HANDLE} "
196+ session_id=" "
197+
198+ initialize_payload=' {"jsonrpc":"2.0","id":"init-doc","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"emcp-demo-verify","version":"1.0.0"}}}'
199+ tools_list_payload=' {"jsonrpc":"2.0","id":"tools-doc","method":"tools/list","params":{}}'
200+ content_search_payload=' {"jsonrpc":"2.0","id":"search-doc","method":"tools/call","params":{"name":"evo.content.search","arguments":{"limit":3,"offset":0}}}'
201+ content_root_tree_payload=' {"jsonrpc":"2.0","id":"root-doc","method":"tools/call","params":{"name":"evo.content.root_tree","arguments":{"limit":3,"offset":0,"depth":2}}}'
202+ content_get_payload=' {"jsonrpc":"2.0","id":"get-doc","method":"tools/call","params":{"name":"evo.content.get","arguments":{"id":1}}}'
203+
204+ init_headers_file=" ${TMP_DIR} /init.headers"
205+ init_raw=$( curl -sS -D " ${init_headers_file} " \
206+ -H ' Content-Type: application/json' \
207+ -H " Authorization: Bearer ${token} " \
208+ -d " ${initialize_payload} " \
209+ -w " \n__HTTP_CODE__:%{http_code}" \
210+ " ${mcp_url} " || true)
211+ init_http_code=$( printf ' %s' " ${init_raw} " | sed -n ' s/^__HTTP_CODE__://p' | tail -n 1)
212+ init_response=$( printf ' %s' " ${init_raw} " | sed ' /^__HTTP_CODE__:/d' )
213+ session_id=$( awk ' BEGIN{IGNORECASE=1} /^MCP-Session-Id:/{gsub("\r","",$2); print $2}' " ${init_headers_file} " | tail -n 1)
214+
215+ mcp_post_with_optional_session () {
216+ payload=" $1 "
217+ headers_file=" $2 "
218+ if [ -n " ${session_id} " ]; then
219+ curl -sS -D " ${headers_file} " \
220+ -H ' Content-Type: application/json' \
221+ -H " Authorization: Bearer ${token} " \
222+ -H " MCP-Session-Id: ${session_id} " \
223+ -d " ${payload} " \
224+ -w " \n__HTTP_CODE__:%{http_code}" \
225+ " ${mcp_url} " || true
226+ return
227+ fi
228+
229+ curl -sS -D " ${headers_file} " \
230+ -H ' Content-Type: application/json' \
231+ -H " Authorization: Bearer ${token} " \
232+ -d " ${payload} " \
233+ -w " \n__HTTP_CODE__:%{http_code}" \
234+ " ${mcp_url} " || true
235+ }
236+
237+ tools_headers_file=" ${TMP_DIR} /tools.headers"
238+ tools_raw=$( mcp_post_with_optional_session " ${tools_list_payload} " " ${tools_headers_file} " )
239+ tools_http_code=$( printf ' %s' " ${tools_raw} " | sed -n ' s/^__HTTP_CODE__://p' | tail -n 1)
240+ tools_response=$( printf ' %s' " ${tools_raw} " | sed ' /^__HTTP_CODE__:/d' )
241+
242+ search_headers_file=" ${TMP_DIR} /search.headers"
243+ search_raw=$( mcp_post_with_optional_session " ${content_search_payload} " " ${search_headers_file} " )
244+ search_http_code=$( printf ' %s' " ${search_raw} " | sed -n ' s/^__HTTP_CODE__://p' | tail -n 1)
245+ search_response=$( printf ' %s' " ${search_raw} " | sed ' /^__HTTP_CODE__:/d' )
246+
247+ root_headers_file=" ${TMP_DIR} /root.headers"
248+ root_raw=$( mcp_post_with_optional_session " ${content_root_tree_payload} " " ${root_headers_file} " )
249+ root_http_code=$( printf ' %s' " ${root_raw} " | sed -n ' s/^__HTTP_CODE__://p' | tail -n 1)
250+ root_response=$( printf ' %s' " ${root_raw} " | sed ' /^__HTTP_CODE__:/d' )
251+
252+ get_headers_file=" ${TMP_DIR} /get.headers"
253+ get_raw=$( mcp_post_with_optional_session " ${content_get_payload} " " ${get_headers_file} " )
254+ get_http_code=$( printf ' %s' " ${get_raw} " | sed -n ' s/^__HTTP_CODE__://p' | tail -n 1)
255+ get_response=$( printf ' %s' " ${get_raw} " | sed ' /^__HTTP_CODE__:/d' )
256+
173257echo " [demo-verify] Step 4/4: running full test suite with runtime HTTP integration"
258+
259+ set +e
174260EMCP_INTEGRATION_ENABLED=1 \
175261EMCP_BASE_URL=" ${DEMO_BASE_URL} " \
176262EMCP_API_PATH=" ${api_prefix} /mcp/{server}" \
177263EMCP_API_TOKEN=" ${token} " \
178264EMCP_SERVER_HANDLE=" ${EMCP_SERVER_HANDLE} " \
179265EMCP_DISPATCH_CHECK=" ${EMCP_DISPATCH_CHECK} " \
180266composer run test
267+ test_exit=$?
268+ set -e
269+
270+ test_status=" PASS"
271+ if [ " ${test_exit} " -ne 0 ]; then
272+ test_status=" FAIL"
273+ fi
274+
275+ run_utc_ts=$( date -u +" %Y-%m-%dT%H:%M:%SZ" )
276+
277+ cat > " ${LOGS_MD} " << EOF
278+ # eMCP Demo Verify Log
279+
280+ Generated at (UTC): \` ${run_utc_ts} \`
281+ Result: **${test_status} ** (exit code: \` ${test_exit} \` )
282+
283+ ## Environment
284+
285+ - Base URL: \` ${DEMO_BASE_URL} \`
286+ - API prefix: \` ${api_prefix} \`
287+ - MCP endpoint: \` ${mcp_url} \`
288+ - Server handle: \` ${EMCP_SERVER_HANDLE} \`
289+ - Token endpoint: \` ${DEMO_BASE_URL}${api_prefix} /token\`
290+ - Token (masked): \` ${token_masked} \`
291+ - MCP Session ID: \` ${session_id:- n/ a} \`
292+ - php -S log file: \` ${SERVER_LOG} \`
293+
294+ ## Smoke Check
295+
296+ \` php artisan emcp:test\` : passed (initialize/tools:list OK)
297+
298+ ## HTTP / MCP Probe Requests
299+
300+ ### 1) initialize
301+
302+ Request:
303+ \`\`\` json
304+ ${initialize_payload}
305+ \`\`\`
306+
307+ HTTP: \` ${init_http_code} \`
308+
309+ Response:
310+ \`\`\` json
311+ ${init_response}
312+ \`\`\`
313+
314+ ### 2) tools/list
315+
316+ Request:
317+ \`\`\` json
318+ ${tools_list_payload}
319+ \`\`\`
320+
321+ HTTP: \` ${tools_http_code} \`
322+
323+ Response:
324+ \`\`\` json
325+ ${tools_response}
326+ \`\`\`
327+
328+ ### 3) site content via evo.content.search
329+
330+ Request:
331+ \`\`\` json
332+ ${content_search_payload}
333+ \`\`\`
334+
335+ HTTP: \` ${search_http_code} \`
336+
337+ Response:
338+ \`\`\` json
339+ ${search_response}
340+ \`\`\`
341+
342+ ### 4) site content via evo.content.root_tree
343+
344+ Request:
345+ \`\`\` json
346+ ${content_root_tree_payload}
347+ \`\`\`
348+
349+ HTTP: \` ${root_http_code} \`
350+
351+ Response:
352+ \`\`\` json
353+ ${root_response}
354+ \`\`\`
355+
356+ ### 5) site content via evo.content.get (id=1)
357+
358+ Request:
359+ \`\`\` json
360+ ${content_get_payload}
361+ \`\`\`
362+
363+ HTTP: \` ${get_http_code} \`
364+
365+ Response:
366+ \`\`\` json
367+ ${get_response}
368+ \`\`\`
369+
370+ ## How To Verify Manually
371+
372+ 1. Get token:
373+ \`\`\` bash
374+ curl -sS -H 'Content-Type: application/json' \\
375+ -d '{"username":"${DEMO_ADMIN_USERNAME} ","password":"${DEMO_ADMIN_PASSWORD} "}' \\
376+ '${DEMO_BASE_URL}${api_prefix} /token'
377+ \`\`\`
378+
379+ 2. Initialize MCP:
380+ \`\`\` bash
381+ curl -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' \\
382+ -d '${initialize_payload} ' \\
383+ '${mcp_url} '
384+ \`\`\`
385+
386+ 3. Read site content (search):
387+ \`\`\` bash
388+ curl -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' \\
389+ -d '${content_search_payload} ' \\
390+ '${mcp_url} '
391+ \`\`\`
392+
393+ 4. Read one document (id=1):
394+ \`\`\` bash
395+ curl -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>' \\
396+ -d '${content_get_payload} ' \\
397+ '${mcp_url} '
398+ \`\`\`
399+ EOF
400+
401+ echo " [demo-verify] Wrote detailed run log: ${LOGS_MD} "
402+
403+ if [ " ${test_exit} " -ne 0 ]; then
404+ exit " ${test_exit} "
405+ fi
181406
182407echo " [demo-verify] PASS: demo is running and MCP checks are green."
0 commit comments