01
Install prerequisites and choose the endpoint
Use `curl`, `jq`, and Python 3 for the command-line tutorial. The public MCP endpoint is used for OAuth-based access; local development can point to `http://localhost:8081/mcp` if you are running the MCP server yourself.
command -v curl
command -v jq
command -v python3
export DOCINTEL_MCP_URL="https://mcp.docintel.adar.agomoniai.com/mcp"
export DOCINTEL_MCP_ISSUER_URL="https://auth.docintel.adar.agomoniai.com"
export MCP_PROTOCOL_VERSION="2025-06-18"
02
Run OAuth login with PKCE, browser login, email MFA, and scoped consent
The helper performs protected-resource discovery, authorization-server discovery, dynamic client registration, PKCE generation, local callback handling, browser login, email MFA, scope validation, state validation, token exchange, and token export. On success it exports `MCP_ACCESS_TOKEN`, `MCP_REFRESH_TOKEN`, `CLIENT_ID`, and endpoint variables, and it defines `mcp_request`, `mcp_tool`, and `tool_data` for the rest of the tutorial.
cd /path/to/adar-rag
unset MCP_ACCESS_TOKEN MCP_REFRESH_TOKEN DOCINTEL_ACCESS_TOKEN
source mcp-server/scripts/oauth_login.sh
type mcp_request
type mcp_tool
type tool_data
echo "MCP token scope: $MCP_TOKEN_SCOPE"
echo "Token loaded: ${#MCP_ACCESS_TOKEN} characters"
# Refresh later without another browser login:
docintel_mcp_refresh_token
# Request a narrower workflow profile when needed:
export DOCINTEL_MCP_SCOPES="workspaces:read documents:read knowledge:query knowledge:generate"
source mcp-server/scripts/oauth_login.sh
# Request content operations when upload, sessions, video, or batch jobs are needed:
export DOCINTEL_MCP_SCOPES="workspaces:read documents:read documents:write knowledge:query knowledge:generate sessions:write video:read video:process batches:read batches:write"
source mcp-server/scripts/oauth_login.sh
# Request governed workflows when vertical workflow review or packet generation is needed:
export DOCINTEL_MCP_SCOPES="workspaces:read documents:read documents:write knowledge:query knowledge:generate sessions:write workflows:read workflows:write reviews:write reviews:approve packets:write"
source mcp-server/scripts/oauth_login.sh
If a requested scope is not assigned to the user, DocIntel creates a pending access request. An administrator can approve, deny, or revoke grants from MCP Access administration. Existing tokens do not gain newly approved scopes automatically; repeat OAuth login in CLI or choose **Update access** in MCP Playground.
03
Keep these reference scripts with the tutorial
The `adar-rag` MCP server already includes `mcp-server/scripts/oauth_login.sh`, `mcp-server/scripts/test_oauth_discovery.sh`, and `mcp-server/scripts/test_mcp.sh`. For a self-contained client workspace, keep the scripts below next to your tutorial commands so every later step has the expected shell functions, discovery checks, and smoke-test commands available.
Script 1: `docintel-mcp-client.sh` for OAuth-loaded MCP helpers
Use this after OAuth login, or source it inside another script after `MCP_ACCESS_TOKEN` is available. It provides the helper functions used throughout the tutorial.
#!/usr/bin/env bash
# Save as docintel-mcp-client.sh, then run:
# source ./docintel-mcp-client.sh
export MCP_URL="${MCP_URL:-https://mcp.docintel.adar.agomoniai.com/mcp}"
export MCP_PROTOCOL_VERSION="${MCP_PROTOCOL_VERSION:-2025-06-18}"
mcp_request() {
if [[ -z "${MCP_ACCESS_TOKEN:-}" ]]; then
echo "MCP_ACCESS_TOKEN is not loaded. Run OAuth login first." >&2
return 1
fi
curl -sS -X POST "$MCP_URL" \
-H "Authorization: Bearer $MCP_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: $MCP_PROTOCOL_VERSION" \
--data "$1"
}
mcp_tool() {
if [[ $# -lt 1 ]]; then
echo "Usage: mcp_tool <tool-name> [arguments-json]" >&2
return 1
fi
local name="$1"
local arguments="${2:-{}}"
if ! jq -e . >/dev/null 2>&1 <<<"$arguments"; then
echo "Tool arguments must be valid JSON" >&2
return 1
fi
mcp_request "$(jq -cn \
--arg name "$name" \
--argjson arguments "$arguments" \
'{jsonrpc:"2.0",id:1,method:"tools/call",params:{name:$name,arguments:$arguments}}')"
}
tool_data() {
jq '
if .error then
{ok:false,error:.error}
elif .result.structuredContent.result? then
.result.structuredContent.result
elif .result.structuredContent? then
.result.structuredContent
elif (.result.content[0].text? | type) == "string" then
(.result.content[0].text | try fromjson catch {text:.})
else
.result
end
'
}
Script 2: `docintel-mcp-login-wrapper.sh` for repo-based OAuth login
Use this wrapper when you have the `adar-rag` repository available. It sources the production OAuth script, opens the browser login, completes MFA and consent, and validates that the helper functions are loaded.
#!/usr/bin/env bash
# Save as docintel-mcp-login-wrapper.sh, then run:
# source ./docintel-mcp-login-wrapper.sh /path/to/adar-rag
export ADAR_RAG_DIR="${1:-/path/to/adar-rag}"
export DOCINTEL_MCP_URL="${DOCINTEL_MCP_URL:-https://mcp.docintel.adar.agomoniai.com/mcp}"
export DOCINTEL_MCP_ISSUER_URL="${DOCINTEL_MCP_ISSUER_URL:-https://auth.docintel.adar.agomoniai.com}"
export DOCINTEL_MCP_SCOPES="${DOCINTEL_MCP_SCOPES:-workspaces:read documents:read documents:write knowledge:query knowledge:generate sessions:write video:read video:process}"
if [[ ! -f "$ADAR_RAG_DIR/mcp-server/scripts/oauth_login.sh" ]]; then
echo "Missing $ADAR_RAG_DIR/mcp-server/scripts/oauth_login.sh" >&2
return 1 2>/dev/null || exit 1
fi
cd "$ADAR_RAG_DIR" || return 1
unset MCP_ACCESS_TOKEN MCP_REFRESH_TOKEN DOCINTEL_ACCESS_TOKEN
source mcp-server/scripts/oauth_login.sh
type mcp_request
type mcp_tool
type tool_data
echo "Granted scopes: $MCP_TOKEN_SCOPE"
echo "MCP token loaded: ${#MCP_ACCESS_TOKEN} characters"
Script 3: `docintel-mcp-discovery-smoke.sh` for public OAuth discovery
Run this before tool testing to confirm the public authorization server, protected-resource metadata, and dynamic client registration are available.
#!/usr/bin/env bash
set -euo pipefail
ISSUER="${DOCINTEL_MCP_ISSUER_URL:-https://auth.docintel.adar.agomoniai.com}"
MCP_URL="${DOCINTEL_MCP_URL:-https://mcp.docintel.adar.agomoniai.com/mcp}"
CALLBACK="${DOCINTEL_OAUTH_TEST_CALLBACK:-http://127.0.0.1:8765/callback}"
for command in curl jq; do
command -v "$command" >/dev/null || {
echo "Missing required command: $command" >&2
exit 1
}
done
echo "[1/3] Authorization-server metadata"
curl -fsS "${ISSUER%/}/.well-known/oauth-authorization-server" | jq -e '
.authorization_endpoint and .token_endpoint and .registration_endpoint and
(.code_challenge_methods_supported | index("S256"))
'
echo "[2/3] Protected-resource metadata"
curl -fsS "${MCP_URL%/mcp}/.well-known/oauth-protected-resource/mcp" | jq -e \
--arg issuer "${ISSUER%/}" '
any(.authorization_servers[]?; rtrimstr("/") == ($issuer | rtrimstr("/")))
'
echo "[3/3] Dynamic client registration"
registration="$(curl -fsS -X POST "${ISSUER%/}/register" \
-H "Content-Type: application/json" \
--data "$(jq -cn --arg redirect "$CALLBACK" \
'{client_name:"DocIntel OAuth smoke test",redirect_uris:[$redirect],token_endpoint_auth_method:"none"}')")"
jq . <<<"$registration"
jq -e '.client_id and (.token_endpoint_auth_method == "none")' <<<"$registration" >/dev/null
echo "OAuth discovery and registration checks passed."
Script 4: `docintel-mcp-smoke.sh` for tools, resources, documents, and optional Q&A
This compact smoke test works with the helpers above. It lists tools, resource templates, workspaces, documents, and optionally runs grounded Q&A when `DOCUMENT_ID` is set.
#!/usr/bin/env bash
set -euo pipefail
: "${MCP_ACCESS_TOKEN:?Run OAuth login first and export MCP_ACCESS_TOKEN}"
export MCP_URL="${MCP_URL:-https://mcp.docintel.adar.agomoniai.com/mcp}"
export MCP_PROTOCOL_VERSION="${MCP_PROTOCOL_VERSION:-2025-06-18}"
export WORKSPACE_ID="${WORKSPACE_ID:-}"
export DOCUMENT_ID="${DOCUMENT_ID:-}"
source ./docintel-mcp-client.sh
echo "Initialize"
mcp_request "$(jq -cn \
--arg version "$MCP_PROTOCOL_VERSION" \
'{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:$version,capabilities:{},clientInfo:{name:"docintel-smoke",version:"1.0"}}}')" | jq
echo "Tools"
mcp_request '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| jq -r '.result.tools[]?.name' | sort
echo "Resource templates"
mcp_request '{"jsonrpc":"2.0","id":3,"method":"resources/templates/list","params":{}}' \
| jq -r '.result.resourceTemplates[]?.uriTemplate'
echo "Workspaces"
mcp_tool list_workspaces '{}' | tool_data | jq
echo "Documents"
mcp_tool list_documents "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
'{workspace_id:(if $workspace_id == "" then null else $workspace_id end)}')" \
| tee /tmp/docintel-documents.json | tool_data | jq
if [[ -n "$DOCUMENT_ID" ]]; then
echo "Document metadata"
mcp_tool get_document "$(jq -cn --arg document_id "$DOCUMENT_ID" \
'{document_id:$document_id}')" | tool_data | jq
echo "Grounded Q&A"
mcp_tool search_knowledgebase "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id "$DOCUMENT_ID" \
--arg question "What are the key facts, risks, and next actions?" \
'{
question:$question,
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
document_ids:[$document_id],
redact_pii:false
}')" | tool_data | jq
else
echo "Set DOCUMENT_ID to run document metadata and grounded Q&A checks."
fi
04
Use `mcp_request`, `mcp_tool`, and `tool_data` helpers
Every MCP call is JSON-RPC over HTTP. `mcp_request` sends raw JSON-RPC payloads, `mcp_tool` wraps `tools/call`, and `tool_data` extracts structured MCP tool output whether the server returns structured content or JSON text. Do not paste access tokens into source files or commit them.
mcp_request() {
curl -sS -X POST "${MCP_URL:-https://mcp.docintel.adar.agomoniai.com/mcp}" \
--header "Authorization: Bearer $MCP_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--header "Accept: application/json, text/event-stream" \
--header "MCP-Protocol-Version: 2025-06-18" \
--data "$1"
}
mcp_tool list_workspaces '{}'
mcp_tool list_documents "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
'{workspace_id:(if $workspace_id == "" then null else $workspace_id end)}')" \
| tool_data
05
Initialize MCP, list tools, and list resource templates
Initialization confirms the server protocol and capabilities. Tool discovery shows what actions the client can call, and resource discovery shows URI templates the client can read.
mcp_request "$(jq -cn \
--arg version "$MCP_PROTOCOL_VERSION" \
'{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:$version,capabilities:{},clientInfo:{name:"docintel-client",version:"1.0"}}}')" | jq
mcp_request '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| jq -r '.result.tools[] | [.name, .description] | @tsv'
mcp_request '{"jsonrpc":"2.0","id":3,"method":"resources/templates/list","params":{}}' \
| jq -r '.result.resourceTemplates[] | [.uriTemplate, .name] | @tsv'
06
List workspaces, list documents, inspect metadata, and read resources
Workspace and document IDs are selectors only; DocIntel still checks the authenticated user's permissions. Export the returned IDs and reuse them for grounded Q&A, session, and resource tests.
mcp_tool list_workspaces '{}' \
| tee /tmp/docintel-list-workspaces.json \
| tool_data
# Empty string means personal documents. Use a workspace UUID for workspace-scoped documents.
export WORKSPACE_ID=""
mcp_tool list_documents "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
'{workspace_id:(if $workspace_id == "" then null else $workspace_id end)}')" \
| tee /tmp/docintel-list-documents.json \
| tool_data
export DOCUMENT_ID="<embedded-document-uuid>"
mcp_tool get_document "$(jq -cn \
--arg document_id "$DOCUMENT_ID" \
'{document_id:$document_id}')" | tool_data
mcp_request "$(jq -cn --arg uri "docintel://documents/$DOCUMENT_ID" \
'{jsonrpc:"2.0",id:7,method:"resources/read",params:{uri:$uri}}')" | jq
mcp_request "$(jq -cn --arg uri "docintel://documents/$DOCUMENT_ID/chunks" \
'{jsonrpc:"2.0",id:8,method:"resources/read",params:{uri:$uri}}')" | jq
07
Upload a document through the signed direct-upload flow
DocIntel keeps large file bytes out of the MCP gateway. The client asks for a short-lived signed upload URL, uploads bytes directly to cloud storage, then calls `complete_document_upload` so DocIntel can verify the object and start ingestion.
export FILE_PATH="/absolute/path/to/document.pdf"
export FILE_NAME="$(basename "$FILE_PATH")"
export FILE_SIZE="$(stat -f%z "$FILE_PATH")"
export CONTENT_TYPE="application/pdf"
printf 'File: %s\nSize: %s bytes\nType: %s\nWorkspace: %s\n' \
"$FILE_NAME" "$FILE_SIZE" "$CONTENT_TYPE" "${WORKSPACE_ID:-personal}"
mcp_tool create_document_upload "$(jq -cn \
--arg filename "$FILE_NAME" \
--arg content_type "$CONTENT_TYPE" \
--argjson file_size "$FILE_SIZE" \
--arg workspace_id "$WORKSPACE_ID" \
'{
filename:$filename,
content_type:$content_type,
file_size:$file_size,
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
redact_pii:false
}')" | tee /tmp/docintel-upload-session.json | tool_data
export UPLOAD_RESULT="$(tool_data < /tmp/docintel-upload-session.json)"
export DOCUMENT_ID="$(jq -r '.doc_id' <<<"$UPLOAD_RESULT")"
export UPLOAD_URL="$(jq -r '.upload_url' <<<"$UPLOAD_RESULT")"
export GCS_SOURCE_PATH="$(jq -r '.gcs_source_path' <<<"$UPLOAD_RESULT")"
echo "Document ID: $DOCUMENT_ID"
echo "Storage path: $GCS_SOURCE_PATH"
echo "Upload URL loaded: ${#UPLOAD_URL} characters"
curl --fail-with-body -X PUT "$UPLOAD_URL" \
-H "Content-Type: $CONTENT_TYPE" \
--upload-file "$FILE_PATH"
mcp_tool complete_document_upload "$(jq -cn \
--arg doc_id "$DOCUMENT_ID" \
--arg filename "$FILE_NAME" \
--arg content_type "$CONTENT_TYPE" \
--argjson file_size "$FILE_SIZE" \
--arg gcs_source_path "$GCS_SOURCE_PATH" \
--arg workspace_id "$WORKSPACE_ID" \
'{
doc_id:$doc_id,
filename:$filename,
content_type:$content_type,
file_size:$file_size,
gcs_source_path:$gcs_source_path,
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
redact_pii:false
}')" | tool_data
check_document_status() {
mcp_tool get_ingestion_status "$(jq -cn \
--arg document_id "$DOCUMENT_ID" \
'{document_id:$document_id}')" | tool_data
}
check_document_status
mcp_tool get_document_chunks "$(jq -cn \
--arg document_id "$DOCUMENT_ID" \
'{document_id:$document_id}')" | tool_data
mcp_tool embed_document "$(jq -cn \
--arg document_id "$DOCUMENT_ID" \
'{document_id:$document_id}')" | tool_data
check_document_status
08
Run batch upload, classification, embedding, and workspace summaries
Batch operations are durable asynchronous jobs. MCP returns a `batch_job_id` immediately, while clients poll `get_batch_status`, read `docintel://batches/{batch_job_id}`, or retrieve item-level results later. File bytes still go directly to signed cloud-storage URLs, and upload processing, classification, and embedding are deliberately separate stages.
export FILE_1="/absolute/path/document-one.pdf"
export FILE_2="/absolute/path/document-two.pdf"
UPLOAD_ARGS="$(jq -n \
--arg workspace_id "$WORKSPACE_ID" \
--arg name1 "$(basename "$FILE_1")" \
--arg name2 "$(basename "$FILE_2")" \
--argjson size1 "$(wc -c < "$FILE_1" | tr -d ' ')" \
--argjson size2 "$(wc -c < "$FILE_2" | tr -d ' ')" \
'{
workspace_id:$workspace_id,
redact_pii:false,
files:[
{filename:$name1,content_type:"application/pdf",file_size:$size1},
{filename:$name2,content_type:"application/pdf",file_size:$size2}
]
}')"
mcp_tool create_batch_upload "$UPLOAD_ARGS" \
| tool_data | tee /tmp/docintel-batch-upload.json | jq
export BATCH_JOB_ID="$(jq -r '.batch_job_id' /tmp/docintel-batch-upload.json)"
export DOCUMENT_ID_1="$(jq -r '.files[0].document_id' /tmp/docintel-batch-upload.json)"
export DOCUMENT_ID_2="$(jq -r '.files[1].document_id' /tmp/docintel-batch-upload.json)"
export UPLOAD_URL_1="$(jq -r '.files[0].upload_url' /tmp/docintel-batch-upload.json)"
export UPLOAD_URL_2="$(jq -r '.files[1].upload_url' /tmp/docintel-batch-upload.json)"
curl --fail-with-body -X PUT "$UPLOAD_URL_1" \
-H "Content-Type: application/pdf" \
--upload-file "$FILE_1"
curl --fail-with-body -X PUT "$UPLOAD_URL_2" \
-H "Content-Type: application/pdf" \
--upload-file "$FILE_2"
mcp_tool complete_batch_upload "$(jq -cn \
--arg batch_job_id "$BATCH_JOB_ID" \
--arg document_id_1 "$DOCUMENT_ID_1" \
--arg document_id_2 "$DOCUMENT_ID_2" \
'{batch_job_id:$batch_job_id,document_ids:[$document_id_1,$document_id_2],concurrency:2}')" \
| tool_data | jq
mcp_tool get_batch_status "$(jq -cn \
--arg batch_job_id "$BATCH_JOB_ID" \
'{batch_job_id:$batch_job_id}')" | tool_data | jq
mcp_tool get_batch_results "$(jq -cn \
--arg batch_job_id "$BATCH_JOB_ID" \
'{batch_job_id:$batch_job_id}')" | tool_data | jq
mcp_tool start_batch_classification "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id_1 "$DOCUMENT_ID_1" \
--arg document_id_2 "$DOCUMENT_ID_2" \
'{workspace_id:$workspace_id,document_ids:[$document_id_1,$document_id_2],concurrency:2,force:false}')" \
| tool_data | tee /tmp/docintel-classification.json | jq
export CLASSIFICATION_JOB_ID="$(jq -r '.batch_job_id' /tmp/docintel-classification.json)"
mcp_tool start_batch_embedding "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id_1 "$DOCUMENT_ID_1" \
--arg document_id_2 "$DOCUMENT_ID_2" \
'{workspace_id:$workspace_id,document_ids:[$document_id_1,$document_id_2],concurrency:2,force:false}')" \
| tool_data | tee /tmp/docintel-embedding.json | jq
export EMBED_JOB_ID="$(jq -r '.batch_job_id' /tmp/docintel-embedding.json)"
mcp_tool start_workspace_summary "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
'{workspace_id:$workspace_id,document_ids:[],summary_type:"executive",redact_pii:false,language:"en",concurrency:2}')" \
| tool_data | jq
mcp_request "$(jq -cn --arg uri "docintel://batches/$BATCH_JOB_ID/results" \
'{jsonrpc:"2.0",id:47,method:"resources/read",params:{uri:$uri}}')" | jq
# Optional recovery and control:
mcp_tool retry_batch_failures "$(jq -cn --arg batch_job_id "$BATCH_JOB_ID" '{batch_job_id:$batch_job_id}')" | tool_data | jq
mcp_tool cancel_batch_job "$(jq -cn --arg batch_job_id "$BATCH_JOB_ID" '{batch_job_id:$batch_job_id,confirm:true}')" | tool_data | jq
OAuth clients need `batches:read` and `batches:write`. Existing OAuth sessions must reconnect once to receive the new scopes. Use `force:true` only when completed classification or embedding should deliberately be repeated.
10
Run document summaries and document comparison
Summary and comparison tools collect the existing backend generation streams into structured MCP results while preserving progress and trace IDs. Summary types include executive, detailed, bullets, sections, and custom prompts.
export DOCUMENT_ID_1="$DOCUMENT_ID"
export DOCUMENT_ID_2="<another-embedded-document-id>"
for TYPE in executive detailed bullets sections; do
echo "Testing summary: $TYPE"
mcp_tool summarize_document "$(jq -cn \
--arg id "$DOCUMENT_ID_1" \
--arg type "$TYPE" \
'{document_id:$id,summary_type:$type,redact_pii:false}')" \
| tool_data
done
mcp_tool summarize_document "$(jq -cn \
--arg id "$DOCUMENT_ID_1" \
'{
document_id:$id,
summary_type:"custom",
custom_prompt:"Identify key facts, risks, decisions, and required actions.",
redact_pii:false
}')" | tool_data
mcp_tool summarize_documents "$(jq -cn \
--arg first "$DOCUMENT_ID_1" \
--arg second "$DOCUMENT_ID_2" \
'{document_ids:[$first,$second],summary_type:"executive",redact_pii:false}')" | tool_data
mcp_tool compare_documents "$(jq -cn \
--arg first "$DOCUMENT_ID_1" \
--arg second "$DOCUMENT_ID_2" \
'{document_id_1:$first,document_id_2:$second,redact_pii:false}')" | tool_data
09
Ask grounded questions and create persistent chat sessions
Grounded search uses DocIntel hybrid retrieval and re-ranking. Session-backed Q&A can persist context inside a DocIntel chat session so an MCP client can continue a contextual conversation across follow-up questions.
mcp_tool search_knowledgebase "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id "$DOCUMENT_ID" \
--arg question "Summarize the important facts, risks, and next actions." \
'{
question:$question,
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
document_ids:[$document_id],
redact_pii:false
}')" | tool_data
mcp_tool create_chat_session "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id "$DOCUMENT_ID" \
'{
title:"MCP tutorial session",
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
document_ids:[$document_id]
}')" | tee /tmp/docintel-create-session.json | tool_data
export SESSION_ID="<returned-session-uuid>"
mcp_tool ask "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id "$DOCUMENT_ID" \
--arg session_id "$SESSION_ID" \
--arg question "What is the most important follow-up question?" \
'{
question:$question,
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
document_ids:[$document_id],
session_id:$session_id,
redact_pii:false
}')" | tool_data
mcp_request "$(jq -cn --arg uri "docintel://sessions/$SESSION_ID" \
'{jsonrpc:"2.0",id:35,method:"resources/read",params:{uri:$uri}}')" | jq
# Optional cleanup requires explicit confirmation and removes related records.
mcp_tool delete_document "$(jq -cn \
--arg document_id "$DOCUMENT_ID" \
'{document_id:$document_id,confirm:true}')" | tool_data
10
Record a governed conversation and publish it as searchable knowledge
Conversation Recording Assistant through MCP covers consent, transcribed turns, completion, review, approval, publication, status, resource reads, and deletion. Audio capture and speech-to-text can happen in the client; MCP carries the resulting transcript through DocIntel's governed lifecycle so the approved conversation becomes a document, chunks, embeddings, and workspace-searchable knowledge.
# Required scopes:
# sessions:write reviews:approve knowledge:query
START_RESPONSE="$(mcp_tool start_conversation_recording "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
'{workspace_id:$workspace_id,language_code:"en-US"}'
)")"
printf '%s\n' "$START_RESPONSE" | tool_data | tee /tmp/conversation-start.json | jq
export CONVERSATION_SESSION_ID="$(jq -r '.id // .session_id // empty' /tmp/conversation-start.json)"
mcp_tool confirm_conversation_consent "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
'{session_id:$id,confirmed:true}'
)" | tool_data | jq
mcp_tool add_conversation_turn "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
--arg text "I am calling to provide an update about the account." \
'{session_id:$id,transcript:$text}'
)" | tool_data | jq
# Repeat add_conversation_turn for each utterance. The response contains the
# assistant's next prompt and updated collection state.
mcp_tool finish_conversation_recording "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
'{session_id:$id}'
)" | tool_data | jq
mcp_tool get_conversation_recording "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
'{session_id:$id}'
)" | tool_data | tee /tmp/conversation-review.json | jq
mcp_request "$(jq -cn \
--arg uri "docintel://conversations/$CONVERSATION_SESSION_ID/transcript" \
'{jsonrpc:"2.0",id:1,method:"resources/read",params:{uri:$uri}}'
)" | jq
export REVIEWED_TRANSCRIPT="$(jq -r '.editable_transcript // empty' /tmp/conversation-review.json)"
mcp_tool approve_conversation_transcript "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
--arg transcript "$REVIEWED_TRANSCRIPT" \
'{session_id:$id,transcript:$transcript,confirm:true}'
)" | tool_data | jq
mcp_tool get_conversation_recording "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
'{session_id:$id}'
)" | tool_data | tee /tmp/conversation-published.json | jq
export DOCUMENT_ID="$(jq -r '.document_id // empty' /tmp/conversation-published.json)"
mcp_tool search_knowledgebase "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id "$DOCUMENT_ID" \
'{
question:"What information was provided and what should happen next?",
workspace_id:$workspace_id,
document_ids:[$document_id],
history:[],
redact_pii:false
}')" | tool_data | jq
mcp_tool list_conversation_recordings "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
'{workspace_id:$workspace_id}'
)" | tool_data | jq
# Optional cleanup removes the recording, transcript document, chunks, vectors,
# and derived records through the existing DocIntel deletion lifecycle.
mcp_tool delete_conversation_recording "$(jq -cn \
--arg id "$CONVERSATION_SESSION_ID" \
'{session_id:$id,confirm:true}'
)" | tool_data | jq
11
Process video intelligence and search timestamped knowledge
Video tools reuse DocIntel's direct upload, transcript, frame sampling, timeline segmentation, embedding, and timestamp-grounded Q&A pipeline. Once processed, the video resource can expose metadata, timeline, transcript, frames, and searchable video context.
export VIDEO_PATH="/absolute/path/to/video.mp4"
export VIDEO_NAME="$(basename "$VIDEO_PATH")"
export VIDEO_SIZE="$(stat -f%z "$VIDEO_PATH")"
export VIDEO_CONTENT_TYPE="video/mp4"
mcp_tool create_video_upload "$(jq -cn \
--arg filename "$VIDEO_NAME" \
--arg content_type "$VIDEO_CONTENT_TYPE" \
--argjson file_size "$VIDEO_SIZE" \
--arg workspace_id "$WORKSPACE_ID" \
'{
filename:$filename,
content_type:$content_type,
file_size:$file_size,
workspace_id:(if $workspace_id == "" then null else $workspace_id end)
}')" | tee /tmp/docintel-video-upload.json | tool_data
export VIDEO_RESULT="$(tool_data < /tmp/docintel-video-upload.json)"
export VIDEO_ID="$(jq -r '.doc_id' <<<"$VIDEO_RESULT")"
export VIDEO_UPLOAD_URL="$(jq -r '.upload_url' <<<"$VIDEO_RESULT")"
export VIDEO_GCS_PATH="$(jq -r '.gcs_source_path' <<<"$VIDEO_RESULT")"
curl --fail-with-body -X PUT "$VIDEO_UPLOAD_URL" \
-H "Content-Type: $VIDEO_CONTENT_TYPE" \
--upload-file "$VIDEO_PATH"
mcp_tool complete_video_upload "$(jq -cn \
--arg doc_id "$VIDEO_ID" \
--arg filename "$VIDEO_NAME" \
--arg content_type "$VIDEO_CONTENT_TYPE" \
--argjson file_size "$VIDEO_SIZE" \
--arg gcs_source_path "$VIDEO_GCS_PATH" \
--arg workspace_id "$WORKSPACE_ID" \
'{
doc_id:$doc_id,
filename:$filename,
content_type:$content_type,
file_size:$file_size,
gcs_source_path:$gcs_source_path,
workspace_id:(if $workspace_id == "" then null else $workspace_id end),
process_after_upload:false
}')" | tool_data
mcp_tool process_video "$(jq -cn \
--arg id "$VIDEO_ID" \
'{
document_id:$id,
rights_confirmed:true,
transcript_language:"auto",
max_frames:12,
segment_seconds:60,
embed_after_processing:true
}')" | tool_data
# Use en-US, hi-IN, bn-IN, ur-PK, or another configured language code
# instead of auto when the video's language is known.
while true; do
RESULT="$(mcp_tool get_video_status "$(jq -cn \
--arg id "$VIDEO_ID" '{document_id:$id}')" | tool_data)"
jq '{
processing_status,
progress_step,
progress_pct,
progress_message,
progress_updated_at,
error_message,
document_error
}' <<<"$RESULT"
STATUS="$(jq -r '.processing_status // empty' <<<"$RESULT")"
[[ "$STATUS" == "completed" || "$STATUS" == "error" ]] && break
sleep 15
done
mcp_tool get_video_timeline "$(jq -cn --arg id "$VIDEO_ID" '{document_id:$id}')" | tool_data
mcp_tool get_video_transcript "$(jq -cn --arg id "$VIDEO_ID" '{document_id:$id}')" | tool_data
mcp_tool get_video_frames "$(jq -cn --arg id "$VIDEO_ID" '{document_id:$id}')" | tool_data
mcp_tool get_video_frame_url "$(jq -cn \
--arg id "$VIDEO_ID" \
'{document_id:$id,frame_index:0}')" | tool_data
mcp_tool search_video "$(jq -cn \
--arg id "$VIDEO_ID" \
'{
document_id:$id,
question:"What happens between 1:00 and 3:00?",
limit:8
}')" | tool_data
mcp_request "$(jq -cn --arg uri "docintel://videos/$VIDEO_ID/timeline" \
'{jsonrpc:"2.0",id:45,method:"resources/read",params:{uri:$uri}}')" | jq
12
Run vertical workflows, human review, approval, and PDF packets
Vertical workflow tools expose industry-specific DocIntel automation through MCP. Healthcare prior authorization is the most complete example: the client discovers the catalog, starts a run with encounter and policy documents, polls status, saves a human review, explicitly approves with `confirm:true`, generates a PDF packet, embeds the generated artifact, and asks grounded questions against the approved packet.
mcp_tool list_vertical_workflows '{}' | tool_data | jq
export ENCOUNTER_DOCUMENT_ID="<encounter-document-id>"
export POLICY_DOCUMENT_ID="<payer-policy-document-id>"
mcp_tool start_vertical_workflow "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg encounter "$ENCOUNTER_DOCUMENT_ID" \
--arg policy "$POLICY_DOCUMENT_ID" \
'{
workflow:"healthcare_prior_auth",
workspace_id:$workspace_id,
document_ids:[$encounter,$policy],
inputs:{policy_document_ids:[$policy]}
}')" | tool_data | tee /tmp/prior-auth-run.json | jq
export RUN_ID="$(jq -r '.run_id // .id // .agent_run.run_id // .agent_run.id' /tmp/prior-auth-run.json)"
mcp_tool get_vertical_run "$(jq -cn \
--arg run_id "$RUN_ID" \
'{vertical:"healthcare",run_id:$run_id}')" | tool_data | tee /tmp/prior-auth-latest.json | jq
jq '.result.review_packet // .result_data.review_packet // .review_packet' \
/tmp/prior-auth-latest.json > /tmp/review-packet.json
mcp_tool save_vertical_review "$(jq -cn \
--arg run_id "$RUN_ID" \
--slurpfile packet /tmp/review-packet.json \
'{
vertical:"healthcare",
run_id:$run_id,
packet:$packet[0],
notes:"Prior authorization packet reviewed through MCP."
}')" | tool_data | jq
mcp_tool approve_vertical_run "$(jq -cn \
--arg run_id "$RUN_ID" \
--slurpfile packet /tmp/review-packet.json \
'{
vertical:"healthcare",
run_id:$run_id,
packet:$packet[0],
notes:"Human review completed and packet approved.",
confirm:true
}')" | tool_data | jq
mcp_tool generate_vertical_packet "$(jq -cn \
--arg run_id "$RUN_ID" \
'{vertical:"healthcare",run_id:$run_id,packet_type:"prior_auth"}')" \
| tool_data | tee /tmp/generated-packet.json | jq
export PACKET_DOCUMENT_ID="$(jq -r '.document.doc_id // .document.id' /tmp/generated-packet.json)"
mcp_tool embed_document "$(jq -cn \
--arg id "$PACKET_DOCUMENT_ID" \
'{document_id:$id}')" | tool_data | jq
mcp_tool search_knowledgebase "$(jq -cn \
--arg workspace_id "$WORKSPACE_ID" \
--arg document_id "$PACKET_DOCUMENT_ID" \
'{
question:"Summarize the requested service, supporting evidence, missing information, and next actions.",
workspace_id:$workspace_id,
document_ids:[$document_id],
redact_pii:false
}')" | tool_data | jq
Other supported workflow names include `healthcare_clinical`, `finance_tax_readiness`, `talent_readiness`, `employee_mobility`, and `lease_intelligence`. Use `list_vertical_workflows` as the authoritative catalog because required inputs, review support, approval support, and packet types can vary by workflow.
13
Run a governed Knowledge Academy learning path
Create a course as a teacher or administrator, save modules and lessons, enroll workspace users, map embedded multimodal content, ask the scoped AI Tutor, generate study material, persist quiz attempts, and review progress. Course authorization remains separate from OAuth scope approval.
mcp_tool create_learning_course '{"workspace_id":"YOUR_WORKSPACE_ID","title":"AI Foundations","course_code":"AI-101","semester":"Fall 2026","description":"Evidence-grounded AI learning","instructor_name":"Course Instructor","objectives":["Explain RAG","Evaluate grounded answers"]}' \
| tool_data | tee /tmp/learning-course.json | jq
export COURSE_ID="$(jq -r '.id' /tmp/learning-course.json)"
mcp_tool save_learning_curriculum "$(jq -cn --arg id "$COURSE_ID" \
'{course_id:$id,modules:[{title:"RAG Foundations",description:"Retrieval and grounding",lessons:[{title:"Hybrid Retrieval",description:"Keyword and vector retrieval"}]}]}')" \
| tool_data | tee /tmp/learning-curriculum.json | jq
export MODULE_ID="$(jq -r '.modules[0].id' /tmp/learning-curriculum.json)"
export LESSON_ID="$(jq -r '.modules[0].lessons[0].id' /tmp/learning-curriculum.json)"
mcp_tool attach_learning_content "$(jq -cn --arg course "$COURSE_ID" --arg document "$DOCUMENT_ID" --arg module "$MODULE_ID" --arg lesson "$LESSON_ID" \
'{course_id:$course,document_id:$document,module_id:$module,lesson_id:$lesson,title:"Hybrid Retrieval Lesson"}')" \
| tool_data | jq
mcp_tool ask_learning_tutor "$(jq -cn --arg course "$COURSE_ID" --arg module "$MODULE_ID" --arg lesson "$LESSON_ID" \
'{course_id:$course,module_id:$module,lesson_id:$lesson,question:"How does hybrid retrieval improve grounding?",history:[],response_language:"en"}')" \
| tool_data | jq
mcp_tool generate_learning_artifact "$(jq -cn --arg course "$COURSE_ID" --arg module "$MODULE_ID" --arg lesson "$LESSON_ID" \
'{course_id:$course,module_id:$module,lesson_id:$lesson,artifact_type:"practice_questions",title:"Hybrid Retrieval Practice",custom_instruction:""}')" \
| tool_data | tee /tmp/learning-artifact.json | jq
export ARTIFACT_ID="$(jq -r '.artifact.id // .id // empty' /tmp/learning-artifact.json)"
mcp_tool get_learning_progress "$(jq -cn --arg id "$COURSE_ID" '{course_id:$id}')" | tool_data | jq
The complete validation continues through dynamic quiz answers, grading, retakes, teacher or advisor escalation, learning resource reads, persona boundaries, traces, events, UI checks, and cleanup.
14
Use a small Python MCP request helper
Python clients can call the same JSON-RPC methods directly. This example keeps the token in an environment variable, initializes the server, lists tools, and asks a grounded question using `tools/call`.
import json
import os
import requests
MCP_URL = os.environ["DOCINTEL_MCP_URL"]
TOKEN = os.environ["MCP_ACCESS_TOKEN"]
PROTOCOL = os.environ.get("MCP_PROTOCOL_VERSION", "2025-06-18")
def mcp_request(method, params=None, request_id=1):
response = requests.post(
MCP_URL,
headers={
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
"MCP-Protocol-Version": PROTOCOL,
},
data=json.dumps({
"jsonrpc": "2.0",
"id": request_id,
"method": method,
"params": params or {},
}),
timeout=120,
)
response.raise_for_status()
payload = response.json()
if payload.get("error"):
raise RuntimeError(payload["error"])
return payload["result"]
print(mcp_request("initialize", {
"protocolVersion": PROTOCOL,
"capabilities": {},
"clientInfo": {"name": "python-docintel-client", "version": "1.0"},
}, 1))
tools = mcp_request("tools/list", {}, 2)
print([tool["name"] for tool in tools.get("tools", [])])
answer = mcp_request("tools/call", {
"name": "search_knowledgebase",
"arguments": {
"question": "What are the key risks and next actions?",
"workspace_id": os.environ["WORKSPACE_ID"],
"document_ids": [os.environ["DOCUMENT_ID"]],
"redact_pii": False,
},
}, 3)
print(json.dumps(answer, indent=2))
15
Run smoke tests and negative authorization checks
Production readiness should include discovery checks, tool coverage, resource coverage, workspace isolation, invalid-token handling, expired token handling, missing-scope handling, revoked-client handling, and cross-workspace rejection. The smoke test script validates the expected MCP tool/resource surface and basic grounded workflows.
cd /path/to/adar-rag
# Discovery and public readiness:
chmod +x mcp-server/scripts/test_oauth_discovery.sh
DOCINTEL_MCP_ISSUER_URL="https://auth.docintel.adar.agomoniai.com" \
DOCINTEL_MCP_URL="https://mcp.docintel.adar.agomoniai.com/mcp" \
mcp-server/scripts/test_oauth_discovery.sh
# Full smoke test after OAuth:
export DOCINTEL_ACCESS_TOKEN="$MCP_ACCESS_TOKEN"
WORKSPACE_ID="$WORKSPACE_ID" DOCUMENT_ID="$DOCUMENT_ID" RUN_SESSION=true \
mcp-server/scripts/test_mcp.sh
# Negative test: invalid token should fail cleanly.
MCP_ACCESS_TOKEN="invalid-token" \
mcp_request '{"jsonrpc":"2.0","id":99,"method":"tools/list","params":{}}' | jq