Skip to content

kayoslab/virgil

Repository files navigation

Virgil

e io sarò tua guida — Inferno, Canto I

Dante's guide through Hell. In the poem he leads the pilgrim down through the nine circles without losing the way; here he leads you through SharePoint's maze of sites, folders, documents, and metadata without losing the file. Same job description, fewer damned souls, marginally better error messages.

Virgil is a read and write agent over Microsoft SharePoint. Ask it to find a file, read one, or drop a new one into a document library — in plain language, and strictly as the person who asked. It does not see more than you can see. A good guide only takes you where you're allowed to go.

Built on the eve agent framework, deployed as a Next.js + eve hybrid on Vercel.

What it does

  • Finds sites and files across SharePoint that the signed-in user is allowed to reach. No keyword archaeology, no "which of the fourteen 'Final_v3' folders."
  • Reads the contents of text-based documents so it can answer, quote, or summarize — with a citation back to the file, because unsourced claims are how you end up in one of the lower circles.
  • Writes new files (or overwrites existing ones) into a document library. Writes change your SharePoint, so every write waits for your explicit approval first. The guide points; you take the step.

Everything runs as the signed-in user through Microsoft Graph. Virgil borrows the user's identity for each request; it has no standing access of its own and cannot lead anyone somewhere they aren't authorized to be.

How it works

You sign in once with Microsoft (Auth.js + Microsoft Entra ID, in the Next app). That single sign-in does double duty: it establishes your browser session and yields the Microsoft Graph access token that the SharePoint tools use on your behalf. No second consent, no separate service credential.

  1. Auth.js signs you in and stores your access + refresh tokens server-side, rotating the access token as it expires (auth.ts).
  2. The chat attaches your current token as a bearer to every eve request (app/_components/agent-chat.tsxGET /api/graph-token).
  3. The eve channel validates that token with a single Graph /me call — which both proves it and identifies you — then hands it to the tools for this request (agent/channels/eve.ts + agent/lib/token-store.ts).
  4. graphFetch (agent/lib/graph.ts) calls Graph as you. A 401 surfaces as a clear "sign in again" message.

Every action is scoped to what your account can do — Virgil has no standing access of its own.

Delegated Graph scopes requested at sign-in. Delegated means the app acts as the signed-in user — effective access is the intersection of the scope and the user's own rights, never tenant-wide (that would be the application permission of the same name).

Scope Why Admin consent
openid profile email Sign-in identity No
offline_access Refresh the token without re-prompting No
User.Read Identify the signed-in user (/me) No
Sites.Read.All Discover & search the user's SharePoint sites Yes
Files.ReadWrite.All Read and upload files the user can access Yes

No site- or list-write scope is requested — the only write is a file upload, covered by Files.ReadWrite.All. The two *.All scopes are high-impact (Microsoft, July 2025) and need a one-time tenant-admin consent; until it's granted, sign-in works and the user's own OneDrive responds, but cross-site SharePoint calls return 403.

The tools

The model never talks to Graph directly. It calls four typed tools, and only the ones it actually invokes ever run:

Tool Reads / Writes What it does
list_sites read Find sites the user can access; returns the site id other tools need.
search_sharepoint read Keyword/phrase search across files and list items; returns driveId+itemId.
get_file_content read Read a text-based file's contents; returns metadata + URL for binary formats.
upload_file write Create or overwrite a file. Approval-gated — confirms before every run.

Architecture

A Next.js + eve hybrid wired together via withEve() in next.config.ts. One Vercel deploy serves the web chat and the agent from a single origin:

  • app/ — Next.js App Router pages (the Web Chat UI).
  • /eve/v1/* — eve channel routes, mounted by withEve().

Layout

agent/                        eve agent
  agent.ts                    runtime config (model: anthropic/claude-sonnet-5)
  instructions.md             always-on system prompt (Virgil's identity + rules)
  channels/eve.ts             route auth: validate the Microsoft bearer via /me → user principal
  lib/token-store.ts          per-request handoff of the user's Graph token to the tools
  lib/graph.ts                graphFetch/graphJson (reads the token from the store)
  tools/
    list_sites.ts             discover accessible sites
    search_sharepoint.ts      search files & list items
    get_file_content.ts       read a file as text
    upload_file.ts            create/overwrite a file (approval-gated)

auth.ts                       Auth.js (Microsoft Entra ID) + refresh-token rotation
proxy.ts                      route guard → redirect to /login when signed out
app/                          Next.js App Router (Web Chat)
  page.tsx                    signed-in guard → chat
  login/page.tsx              "Sign in with Microsoft"
  api/auth/[...nextauth]/     Auth.js endpoints
  api/graph-token/            returns the session's fresh Graph token to the browser
  _components/                chat UI (useEveAgent + bearer header + sign-out)

next.config.ts                withEve() — mounts the eve runtime into Next
vercel.json                   web + eve services, one origin

Local development

npm install

# The agent's terminal REPL (eve HMR dev server):
npm run dev:eve

# ...or the full web chat (Next.js with eve proxied in):
npm run dev

Scripts

Command What it does
npm run dev Next.js dev server (Web Chat, with eve proxied in)
npm run dev:eve eve dev server + terminal REPL
npm run build Next.js production build
npm run build:eve eve build
npm run start Serve the Next.js production build
npm run typecheck tsc type check

Requirements

  • Node.js 24.x

Setup

1. Register a Microsoft Entra ID app (single tenant)

  1. Entra admin center → App registrations → New registration. Name it Virgil; account type Accounts in this organizational directory only; redirect URI Webhttp://localhost:3000/api/auth/callback/microsoft-entra-id.
  2. Copy the Application (client) ID and Directory (tenant) ID.
  3. Certificates & secrets → New client secret → copy the Value.
  4. API permissions → Add → Microsoft Graph → Delegated → add openid, profile, email, offline_access, User.Read, Sites.Read.All, Files.ReadWrite.All, then Grant admin consent (a tenant admin does this once; without it, only the user's own OneDrive responds). These are delegated, so access is bounded by each signed-in user's own rights — not tenant-wide.
  5. After deploying, add the production redirect URI: https://<deployment-domain>/api/auth/callback/microsoft-entra-id.

2. Environment

Copy .env.example to .env.local and fill in:

AUTH_SECRET                     # openssl rand -base64 33
AUTH_MICROSOFT_ENTRA_ID_ID      # client ID
AUTH_MICROSOFT_ENTRA_ID_SECRET  # client secret value
AUTH_MICROSOFT_ENTRA_ID_ISSUER  # https://login.microsoftonline.com/<tenant-id>/v2.0

The model authenticates through the Vercel AI Gateway: on Vercel a linked project uses OIDC automatically; locally, run eve link or set AI_GATEWAY_API_KEY (https://vercel.com/dashboard/ai/api-keys).

Deployment

Deployed on Vercel. Set the four AUTH_* env vars (Production and Preview scope — the same AUTH_SECRET is shared by the web and eve services), add the production redirect URI to the Entra app, then vercel deploy. Preview URLs sit behind Vercel Deployment Protection SSO — test on production or disable protection for the preview.

Status

Wired end to end and building clean: npm run typecheck and npm run build both pass, the eve service registers all four tools with zero discovery errors, and the Microsoft sign-in → bearer → Graph path is in place. Honest caveats: the *.All scopes need the one-time admin consent to reach SharePoint sites; the token store is in-process memory (a shared store like Vercel KV is the scale-up); and there's no evals/ suite, per-tool tests, or channel rate limit yet.

Contributing

Contributions welcome — issues, ideas, and PRs. See CONTRIBUTING.md for setup, the checks to run before opening a PR, and how the pieces fit together. Be kind; the underworld is unpleasant enough.

License

MIT © 2026 cr0ss. Open source — use it, fork it, ferry your own documents.

About

Virgil is a read and write agent over Microsoft SharePoint using Vercel EVE.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages