diff --git a/README.md b/README.md index 358e7a0..ac84644 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,25 @@ -# @context - professional context manager +# @context — your work context, self-hosted -@context is a self-hosted context manager. It organizes your work context into a private CRM and knowledge base and helps you stay on top of things. +@context is a personal work-context proxy: a private CRM and knowledge base with an agent in front of it. Tell it things in plain language (*"met Kyle from Agno, wants a partnership, follow up next week"*) and it files contacts, notes, reminders, and specs for you — then briefs you back on demand (*"what's on today?"*). It plugs into Claude, Claude Code, Codex, ChatGPT, and Cursor over MCP, giving all your AI tools one shared source of truth about your work. -It plugs into clients like claude, chatGPT, claude code, and codex, and gives them a single source of @context about your work. I use it with claude code to manage product specs. +It's built for individuals who run their day across email, Slack, and a pile of AI tools — with privacy as a first principle: everything runs in your own infrastructure, and every byte lives in your own database. -> Your AI tools are users of context, not competitors. +> Built on [Agno](https://docs.agno.com)'s AgentOS runtime. -@context is built with privacy and security as first principles. It runs in two modes: +## How it works -1. **Owner mode.** You get every tool. Capture context (*"met Kyle from Agno, follow up next week"*), retrieve context (*"give me a rundown of my day"*), and prepare context (*"process today"*). -2. **Guest mode.** Teammates (*and their agents*) can leave updates in your queue. You get briefed when you ask for a rundown. +| Piece | What it does | +|---|---| +| **One agent, role-gated tools** ([`agents/context.py`](agents/context.py)) | AgentOS verifies the caller on every request (JWT or Slack identity). The **owner** gets every tool; **guests** get capture-only — they can leave updates in your queue but never read or act. | +| **CRM** (`crm` Postgres schema) | Structured memory: contacts, projects, meetings, reminders, notes. No forms — @context maps what you say onto the right table. See [`docs/CRM.md`](docs/CRM.md). | +| **Knowledge base** (`knowledge/`, or a Git repo) | Specs, research notes, briefs. Filesystem-backed by default; back it with a private Git repo for production. See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md). | +| **Skills** ([`skills/`](skills/)) | Owner playbooks invoked in natural language: daily rundown, week plan, meeting prep, process-today, research, knowledge review. | +| **Workflows + schedules** ([`workflows/`](workflows/), [`app/schedules.py`](app/schedules.py)) | An hourly reminder sweep files due reminders into your queue; daily/weekly digests DM you on Slack when Slack is configured. | +| **External actions, double-gated** | Calendar changes pause for explicit approval (`requires_confirmation`); Gmail can *only* draft — it never sends. See [`docs/SECURITY.md`](docs/SECURITY.md). | -@context runs on Agno's AgentOS runtime, so user identity is verified on every request, and tools are assigned by role (owner or guest). +Guests (teammates and *their* agents) can @-mention your @context on Slack to drop updates in your queue — and your @context can do the same to theirs. That's the [context network](docs/NETWORK.md). -> Built on [Agno](https://docs.agno.com). - -## Job Scope - -@context has five jobs. - -1. **Maintain a CRM.** Share *"met Kyle from Agno, wants a partnership, follow up next week"*, and it stores a contact, a note, and a dated reminder without you picking forms or fields. -2. **Maintain a knowledge base.** @context writes product specs, parses customer interview notes, manages project briefs, and runs deep research, then keeps it all neatly organized in one place. -3. **Run your day, plan your week, prep ahead.** @context runs repeatable playbooks to make things easier. A few come built in, and you should customize them and add your own. Here are the included ones: - - **Rundown** *("what's on today?")*: a prioritized brief of things on your plate. One digest instead of five apps: the updates teammates (and their agents) left in your queue, reminders that are due, today's meetings, the emails you missed, and the Slack threads worth a look. - - **Week plan** *("what's my week?")*: priorities for the week. Runs Sunday evening and lands in your DMs, so you start the week with 🔥 - - **Prep** *("prep for my 2pm with Kyle")*: a tight pre-meeting brief covering who they are, notes, past threads, what's still open, email and Slack exchanges, and public background from the web for contacts you don't know yet. - - @context runs these playbooks on demand or on a schedule. The daily rundown and weekly plan DM the brief straight to you. -4. **Represent you.** Your teammates (and their agents) can share non-urgent updates with your @context. A teammate types *"@your-context my claude fixed the auth bug"*, and it lands in your queue and surfaces in your next rundown. It works outbound too. Your @context can message people and channels on Slack on your behalf, and @-mention a teammate's @context to drop an update in *their* queue. That's how a team's contexts talk to each other (the [context network](docs/NETWORK.md)). This keeps your signal-to-noise high. -5. **Draft and schedule.** Connect [Gmail and Calendar](docs/GOOGLE.md), and @context reads your real inbox and calendar, drafts your follow-ups straight into Gmail for you to send, and sends calendar changes to your approvals queue. - -## Security - -@context is an alter ego with access to a lot of sensitive information, so the security boundaries need to be AIRTIGHT. - -Agno's AgentOS makes two things possible: - -1. **Verify the user making the request.** AgentOS extracts the `user_id` from the JWT or the Slack request, so we can tell whether the caller is the owner or a guest. -2. **Assign tools by role.** Based on that, we add the right tools to the agent. - -This model lets us build a system that anyone can write to, but only you can read from or act through. To everyone else, it is a polite notetaker that only captures. Although it does remember who it is talking to: each caller gets their own user-memory, kept entirely separate from yours. - -External actions are double gated. Changing your calendar (`update_calendar`) pauses for explicit approval before it runs, using AgentOS's `requires_confirmation` and `approval_type="required"` settings. Email goes a step further. `update_gmail` can *only* draft, so the follow-up waits in your Gmail drafts for you to review and send. It never sends on its own. - -Everything runs locally or in your own cloud, inside your VPC. Every byte of data lives in your own database: the CRM, the knowledge base, and the inbox. - -Read [`docs/SECURITY.md`](docs/SECURITY.md) for more details. - -## Get started +## Quick start > Requires [Docker](https://www.docker.com/get-started/) installed and running. @@ -55,336 +27,81 @@ Read [`docs/SECURITY.md`](docs/SECURITY.md) for more details. git clone https://github.com/agno-agi/context.git cd context -# Configure credentials cp example.env .env -# Open .env: set OPENAI_API_KEY, and set OWNER_ID to the email you sign in to os.agno.com with. -# OWNER_NAME is an optional display name, set it as your name. +# In .env: set OPENAI_API_KEY, and set OWNER_ID to the email you sign in to os.agno.com with. -# Run on Docker docker compose up -d --build ``` -Confirm it is live at [http://localhost:8000/docs](http://localhost:8000/docs). - -## AgentOS UI - -@context runs on AgentOS, which comes with a web UI for managing and monitoring @context. Use the AgentOS UI to chat with @context, view sessions, approve actions and more. - -Local Context AgentOS - -1. Open [os.agno.com](https://os.agno.com) and sign in with your email (the same one you set as `OWNER_ID`). -2. Click **Connect AgentOS → Local**. -3. Enter `http://localhost:8000`, name it "Local Context" and connect. -4. Click on the chat button under Context. -5. Try one of the quick prompts. - -## MCP server - -The main way to use @context is from an MCP client like Claude Code, Codex, Claude, Cursor, and ChatGPT. Connect your favorite AI tools to @context's MCP server at `http://localhost:8000/mcp`. - -> Note: @context's MCP server is **owner-only**, so keep an eye on security. - -### Add @context to MCP clients automatically - -Add @context to every MCP client on your machine with one command: - -```sh -python scripts/connect.py -``` - -The script finds Claude Code, Codex, the Claude Desktop app, and Cursor, and registers @context with each. Use `--dry-run` to preview and `--remove` to undo. Once you've deployed, the same script points your clients at the live instance — see [Connect production @context MCP server](#connect-production-context-mcp-server). - -### Add @context to MCP clients manually - -For CLI clients, run the following commands: - -```sh -claude mcp add -s user --transport http context http://localhost:8000/mcp # Claude Code (user scope) - -codex mcp add --url http://localhost:8000/mcp context # Codex -``` - -**Claude Desktop** needs that bridge in `claude_desktop_config.json`, because its "Add custom connector" dialog only accepts `https` URLs. Add this (keep any existing keys) and restart the app: - -```json -{ - "mcpServers": { - "context": { - "command": "npx", - "args": ["-y", "mcp-remote", "http://localhost:8000/mcp", "--transport", "http-only"] - } - } -} -``` - -**Cursor** speaks remote MCP directly (no bridge). Add this to `~/.cursor/mcp.json` and restart Cursor: - -```json -{ - "mcpServers": { - "context": { - "url": "http://localhost:8000/mcp" - } - } -} -``` - -See [`docs/MCP.md`](docs/MCP.md) for more details. - -## Slack - -Slack is where @context comes alive. It's the interface where I (@ashpreetbedi) use it the most and the interface that allows your team (and their agents) to talk to @context. - -To set it up, you need to: -1. Create a Slack app -2. Get the Bot User OAuth Token and Signing Secret -3. Set the environment variables in `.env` or `.env.production` -4. Restart the application +Confirm it's live at [http://localhost:8000/docs](http://localhost:8000/docs). Compose runs two containers: `context-api` (FastAPI + AgentOS, hot-reload in dev mode) and `context-db` (Postgres with pgvector). -Read [`docs/SLACK.md`](docs/SLACK.md) for the Slack setup guide. +## Interfaces -## @context Knowledge Base +**AgentOS web UI.** Open [os.agno.com](https://os.agno.com), sign in with the email you set as `OWNER_ID`, click **Connect AgentOS → Local**, and enter `http://localhost:8000`. Chat with @context, view sessions, and approve actions from there. -@context comes with a knowledge base that acts as its second brain. @context stores everything from product specs and research notes to "what I know about X" pages in this knowledge base. - -The knowledge base is stored on the filesystem by default (a gitignored `knowledge/` folder in this repo) but I highly recommend pointing it to a git repo or notion database for production. See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md) for the full guide. - -Try: -- *Write a one-pager on the advantages of building our own agent-platform* -- *Write up a decision: we're standardizing on agno* -- *What in my knowledge base needs attention?* - -## @context CRM - -@context comes with a CRM that gives it structured memory about people, projects, meetings, reminders, notes and contacts. - -This auto-managing crm is @context's superpower. Use it to manage projects, meetings, reminders, notes, and contacts. @context maps what you tell it onto the right table - no forms, no fields - and can create new tables on demand. Try: -- *"Add Dana Reyes, Head of Platform at Acme, dana@acme.com - and remind me to send her the integration spec next Tuesday."* -- *"Who do I know at Acme?"* -- *"What reminders do I have coming up?"* -- *"Tell me about Northwind."* - -@context's database lives in the `crm` Postgres schema: writes are confined to that schema and every row is scoped to your `user_id`, so a guest can't see this data. See [`docs/CRM.md`](docs/CRM.md) for the schema, the filing rules, and the write boundary. - -## Run in production - -@context runs anywhere that runs a Docker container. - -For a quick deployment, the repo includes a script to run on Railway. The `scripts/railway/up.sh` script will run @context as a service with Postgres on the same private network. It reads credentials from `.env.production`, and creates a public domain you connect to in the AgentOS UI. - -> Requires the [Railway CLI](https://docs.railway.com/cli#installing-the-cli) - -### 1. Production env - -Create the production environment file. +**MCP (Claude Code, Codex, Claude Desktop, Cursor, ChatGPT).** @context ships AgentOS's built-in MCP server at `http://localhost:8000/mcp`, exposing one owner-only tool: `use_context`. Wire up every MCP client on your machine with one command: ```sh -cp .env .env.production +python scripts/connect.py # --dry-run to preview, --remove to undo ``` -The deploy scripts read `.env.production` first and fall back to `.env` if it doesn't exist. +Once deployed, `./scripts/setup_context.sh` mints a self-issued JWT and points your clients at the live instance. Per-client details in [`docs/MCP.md`](docs/MCP.md). -### 2. Deploy +**Slack.** The interface that lets your team (and their agents) talk to @context. Set `SLACK_BOT_TOKEN` and `SLACK_SIGNING_SECRET` and restart; setting the bot token also arms the scheduled daily/weekly digest DMs. Setup guide: [`docs/SLACK.md`](docs/SLACK.md). -Run the `up.sh` script to run @context + postgres on Railway. +## Deploy -First, login to Railway. +@context runs anywhere that runs a Docker container. For Railway (the included path — requires the [Railway CLI](https://docs.railway.com/cli#installing-the-cli)): ```sh +cp .env .env.production # deploy scripts read .env.production, falling back to .env railway login +./scripts/railway/up.sh # @context + Postgres on a private network, public domain for the API ``` -Then run the `up.sh` script. - -```sh -./scripts/railway/up.sh -``` - -The script will now pause and wait for you to mint the JWT verification key. - -### 3. Enable Token Based Authorization +Two things to know: -Token-Based Authorization is on by default. Without the `JWT_VERIFICATION_KEY` environment variable in `.env.production`, the AgentOS will not serve traffic. That is the safe default for an agent that has access to sensitive information. You can issue and verify your own JWT (see [BYO JWT](https://docs.agno.com/agent-os/security/authorization/self-hosted)) or mint a JWT verification key at [os.agno.com](https://os.agno.com). +- **JWT is required at prod boot.** With `RUNTIME_ENV=prd`, AgentOS refuses to serve traffic without `JWT_VERIFICATION_KEY` — the safe default for an agent holding your inbox and CRM. `up.sh` pauses while you mint the key at [os.agno.com](https://os.agno.com) (**Connect AgentOS → Live** → enable **Token Based Authorization**, paste the public key into `.env.production`) or [bring your own JWT](https://docs.agno.com/agent-os/security/authorization/self-hosted). +- **Single-replica scheduler.** The deploy ships `numReplicas: 1` ([`railway.json`](railway.json)); the built-in scheduler assumes it. Before scaling out, read [`docs/SCALING.md`](docs/SCALING.md) and pin `INTERNAL_SERVICE_TOKEN`. -The `up.sh` script pauses and waits for you to add the JWT verification key to `.env.production`. Here's how you can get one from os.agno.com: +Afterwards: `railway logs --service agent-os` to watch it come up, `./scripts/railway/env-sync.sh` to push `.env.production` changes, `./scripts/railway/redeploy.sh` after code changes, and `./scripts/setup_context.sh` to wire your MCP clients to production. If Slack was set up locally, repoint its request URLs to the Railway domain ([`docs/SLACK.md`](docs/SLACK.md#moving-from-local-to-production)). -1. Open [os.agno.com](https://os.agno.com), click **Connect AgentOS → Live** -2. The `up.sh` script will print the AgentOS domain, paste it into the input field. -3. Enable **Token Based Authorization** and click **Connect**. -4. Copy the public key and paste it into `.env.production`. -5. Back in the terminal, press Enter. `up.sh` will read the key and deploy the AgentOS service to Railway. +## Configuration -### 4. Verify - -You can verify the deployment on the Railway dashboard or in the terminal by watching the logs: - -```sh -railway logs --service agent-os -``` +Copy [`example.env`](example.env) to `.env` — it documents every knob. The ones that matter most: -If you add/update any values in `.env.production`, you can sync them to Railway with: +| Variable | Required | Description | +|---|---|---| +| `OPENAI_API_KEY` | yes | Models and embeddings. | +| `OWNER_ID` | prod | Comma-separated identities that count as the owner (JWT `sub` and/or Slack email). First is canonical. Unset means capture-only for everyone. | +| `OWNER_NAME` / `OWNER_TIMEZONE` | no | Display name; IANA timezone anchoring "today" and due-date math (default UTC). | +| `RUNTIME_ENV` | no | `dev` enables hot-reload and disables JWT (compose sets this); default `prd`. | +| `JWT_VERIFICATION_KEY` | prod | Public key from os.agno.com. AgentOS won't serve traffic in `prd` without it. | +| `SLACK_BOT_TOKEN` / `SLACK_SIGNING_SECRET` | no | Enable the Slack interface; the bot token alone activates the `slack` source and arms the digests. | +| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` / `GOOGLE_PROJECT_ID` | no | Gmail + Calendar; mint tokens with `python scripts/google_mint_tokens.py` ([`docs/GOOGLE.md`](docs/GOOGLE.md)). | +| `KNOWLEDGE_REPO_URL` / `KNOWLEDGE_GITHUB_TOKEN` | no | Back the knowledge base with a Git repo instead of local files. | +| `PARALLEL_API_KEY` | no | Authenticated web search (higher rate ceiling); recommended for production. | +| `DAILY_DIGEST_CRON` / `WEEKLY_DIGEST_CRON` | no | UTC crons for the Slack digests (defaults `0 13 * * *` / `0 22 * * 0`). | +| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASS` / `DB_DATABASE` | no | Postgres connection; defaults match compose. | -```sh -./scripts/railway/env-sync.sh -``` +Never commit `.env` or `.env.production` — both are gitignored and carry real keys. -### 5. Redeploy after code changes +## Evals -If you make code changes (which you most definitely will), you can redeploy the AgentOS service to Railway with: +The eval suite ([`evals/`](evals/)) regression-tests the security claim: *anyone can write, only you can read.* It needs a local venv: ```sh -./scripts/railway/redeploy.sh -``` - -Or enable auto-deploy in the Railway dashboard: - -1. Open the Railway dashboard -2. Navigate to the agent-os service -3. Click **Settings** -4. Click **Source** and select the git repo for this project -5. Set the deploy branch to `main` and click **Deploy** +./scripts/venv_setup.sh && source .venv/bin/activate -### 6. Point Slack at production - -If you set Slack up locally, your Slack app's request URLs still point at your local @context via the ngrok tunnel. - -Repoint the `/slack/events` and `/slack/interactions` request URLs to your Railway domain. AgentOS must already be deployed and serving traffic so Slack's URL re-verification passes. - -See [`docs/SLACK.md`](docs/SLACK.md#moving-from-local-to-production) for full steps. - -## Connect production @context MCP server - -Once @context is deployed (you have a Railway domain), point your MCP clients at the production endpoint instead of localhost. The deployed server is JWT-gated, so this needs a bearer token — for the MCP server, we mint our own and push it to Railway. One command does the whole thing: - -```sh -source .venv/bin/activate # mint needs pyjwt + cryptography (in requirements) -./scripts/setup_context.sh # login → mint token → push public key → redeploy → wire clients +python -m evals # full suite +python -m evals -v # stream the agent runs +python -m evals --case # one case ``` -[`scripts/setup_context.sh`](scripts/setup_context.sh) is the single front door. By default it does a full setup: checks `railway login`, mints a fresh token, pushes the public key, runs a `railway up` redeploy (so a `railway.json` change like `numReplicas` lands), wires Claude Code, Codex, Claude Desktop, and Cursor with the token, then tells you to restart your apps. It never restarts apps for you or touches your data. Re-run it any time to rotate the token — add **`--no-redeploy`** to skip the redeploy and just rotate the token + rewire clients. - -Under the hood it chains three pieces you can also run by hand: - -1. [`scripts/mint_mcp_jwt.py`](scripts/mint_mcp_jwt.py) — self-issues an RS256 keypair (private key stays local in gitignored `secrets/`) and writes the public key + a signed admin token to `.env.production`. -2. [`scripts/railway/env-sync.sh`](scripts/railway/env-sync.sh) — pushes the **public** key to Railway so the deploy trusts your token (the token itself stays off the server). -3. [`scripts/connect.py --production`](scripts/connect.py) — threads the token into Claude Code, Codex, Claude Desktop, and Cursor. - -You self-issue the token instead of copying one from os.agno.com, and @context trusts your key *alongside* the os.agno.com one — so the [AgentOS UI](#agentos-ui) keeps working too. - -See [`docs/MCP.md`](docs/MCP.md#self-issued-production-token) for the full details: where the token comes from, per-client specifics (Codex's `$CONTEXT_JWT`, switching local→prod), ChatGPT/Claude web, and how it's secured. - -## Connect @context knowledge base to Git +Exit 0 on all-pass, non-zero otherwise. -When testing locally we can use the local `knowledge/` folder to store the knowledge base. But in production we need to back the knowledge base with a durable solution like a Git repo. - -Here's how to connect @context's knowledge base to a Git repo. - -1. Open [github.com/new](https://github.com/new) and create a new repo for your @context's knowledge base. - - Name: `your-username/your-context` - - Visibility: Mark the repo as private. - - Add README: Yes -2. Mint a GitHub token with push access to the repo. - - Open [github.com/settings/personal-access-tokens](https://github.com/settings/personal-access-tokens) and create a new fine-grained token. - - Click on **Generate new token** - - Name: `Your Context` - - Expiration: **No expiration** - - Repository access: **Only select repositories** and select the repo you created in step 1. - - **Add Permissions** -> **Select Contents** - - Remember to update Access to **read and write** - - Generate token and copy the token. -3. Add both to `.env.production`: - ```sh - KNOWLEDGE_REPO_URL=https://github.com/you/your-specs.git - KNOWLEDGE_GITHUB_TOKEN=ghp_... - ``` -4. Sync to Railway: - ```sh - ./scripts/railway/env-sync.sh - ``` - -See [`docs/KNOWLEDGE.md`](docs/KNOWLEDGE.md) for the full guide. - -## Connect Gmail and Calendar - -You can connect your Gmail and Calendar to @context to ground the rundown and meeting prep in your real inbox and calendar. - -See [`docs/GOOGLE.md`](docs/GOOGLE.md) for more details. - -## Understanding the codebase - -@context has three main components. Review them in order. - -### The app (`app/`) - -@context is a FastAPI application running the AgentOS runtime. [`app/main.py`](app/main.py) is the entrypoint and [`app/settings.py`](app/settings.py) holds shared settings. [`app/identity.py`](app/identity.py) is where identity is validated. It looks dense, but all it does is check whether `user_id` is in the `OWNER_ID` list (comma-separated). [`app/mcp.py`](app/mcp.py) is the owner-only MCP server — one tool (`use_context`) that lets you read, act, and file through @context from the Claude/ChatGPT desktop apps and CLI clients (see [MCP server](#mcp-server)). - -### The agents (`agents/`) - -The main agent is [`agents/context.py`](agents/context.py). `context_tools()` adds tools to the agent based on the caller's role, and `caller_information()` adds the matching instructions. - -The supporting files: - -- [`agents/instructions.py`](agents/instructions.py) defines the role-specific instructions. -- [`agents/sources.py`](agents/sources.py) defines the context providers (crm, knowledge, workspace, web, Slack, Gmail, Calendar) and how each registers its `query_` / `update_` tools. -- [`agents/inbox.py`](agents/inbox.py) defines the inbound queue: `submit_update` (anyone), then `rundown` / `acknowledge` (you only). -- [`agents/policy.py`](agents/policy.py) defines the pre-hook and tool-hook that back the owner/guest boundary. -- [`workflows/`](workflows/) defines the runnable `Workflow` objects (the reminder sweep, the digests) and `dm_owner`; [`app/schedules.py`](app/schedules.py) registers their crons. The reminder sweep (`workflows/reminders.py`) files due reminders into the inbound queue, run hourly by the `queue-reminders` schedule. - -### The skills (`skills/`) - -The repo has **two distinct kinds of skill**. Keep them separate. - -- **Runtime skills** ([`skills/`](skills/)) are playbooks the deployed @context agent runs **for its owner**, invoked in natural language ("plan my week") and owner-gated. Add your own as needed. -- **Coding-agent workflows** ([`.agents/skills/`](.agents/skills/)) are `/slash-command` workflows your *coding agent* (Claude Code, Codex, others) runs while **developing this repo**. They are covered under [Working with coding agents](AGENTS.md#working-with-coding-agents). - -Here are the runtime skills that are included in the repo: - -- [`skills/week-plan/SKILL.md`](skills/week-plan/SKILL.md). -- [`skills/daily-rundown/SKILL.md`](skills/daily-rundown/SKILL.md). -- [`skills/prep-for/SKILL.md`](skills/prep-for/SKILL.md). -- [`skills/process-today/SKILL.md`](skills/process-today/SKILL.md). -- [`skills/research/SKILL.md`](skills/research/SKILL.md). -- [`skills/knowledge-review/SKILL.md`](skills/knowledge-review/SKILL.md). - -## Evals - -@context comes with an eval suite ([`evals/`](evals/)) for regression testing. It's tests the claim that: *anyone can write, only you can read.* - -Run it: - -```sh -python -m evals # run the full suite -python -m evals -v # stream the full agent run -python -m evals --case # one case -``` +## Source / links -## Environment variables - -| Variable | Required | Default | Description | -|----------|----------|---------|-------------| -| `OPENAI_API_KEY` | yes | none | OpenAI key for models and embeddings. | -| `OWNER_ID` | prd | none | Comma-separated identities that count as the owner (JWT `sub` and/or Slack email). First is canonical. Unset means capture-only for everyone. | -| `OWNER_NAME` | no | canonical `OWNER_ID` | Display name rendered into the prompt. Cosmetic, never matched as an identity. | -| `RUNTIME_ENV` | no | `prd` | `dev` enables hot-reload and disables JWT. Compose sets this to `dev` for local. | -| `JWT_VERIFICATION_KEY` | prd | none | Public key from os.agno.com. Required when `RUNTIME_ENV=prd`. | -| `CONTEXT_SELF_VERIFICATION_KEY` | no | none | A second JWT public key the app *also* trusts, alongside the os.agno.com key — your own, so tokens you self-issue with `scripts/mint_mcp_jwt.py` verify (the os.agno.com UI keeps working). Written by that script; pushed to the server by `env-sync.sh`. See [Connect production @context MCP server](#connect-production-context-mcp-server). | -| `CONTEXT_MCP_JWT` | no | none | The self-issued bearer token `scripts/connect.py --production` threads into your MCP clients. Client-side only (lives in `.env.production`, never pushed to the server). Minted by `scripts/mint_mcp_jwt.py`. | -| `AGENTOS_URL` | no | `http://127.0.0.1:8000` | Scheduler base URL. Also anchors the MCP server's Host allowlist — set it to your Railway/ngrok domain so the deployed or tunnelled `/mcp` endpoint accepts that Host (see [`docs/MCP.md`](docs/MCP.md)). | -| `INTERNAL_SERVICE_TOKEN` | no | auto-generated | Scheduler-to-OS auth token. The deploy ships 1 replica, so the auto-generated value is fine; `scripts/railway/up.sh` still pins one so scaling up stays correct (override in `.env.production`). See [`docs/SCALING.md`](docs/SCALING.md). | -| `PARALLEL_API_KEY` | no | none | Switches the `web` source from keyless Parallel MCP to the authenticated SDK (higher rate ceiling); recommended for production. Get a key at [platform.parallel.ai](https://platform.parallel.ai/settings?tab=api-keys). | -| `SLACK_BOT_TOKEN` / `SLACK_SIGNING_SECRET` | no | none | Both enable the Slack interface. The bot token alone activates the `slack` source (`query_slack` + the ungated `update_slack` send tool) and auto-arms the scheduled digests. See [`docs/SLACK.md`](docs/SLACK.md). | -| `DAILY_DIGEST_CRON` / `WEEKLY_DIGEST_CRON` | no | `0 13 * * *` / `0 22 * * 0` | UTC cron for the Slack-delivered daily rundown and weekly plan (only armed when Slack is set). See [`docs/SLACK.md`](docs/SLACK.md). | -| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` / `GOOGLE_PROJECT_ID` | no | none | Connect your Gmail + Calendar; mint tokens with `python scripts/google_mint_tokens.py`. See [`docs/GOOGLE.md`](docs/GOOGLE.md). | -| `GMAIL_TOKEN_JSON_B64` / `CALENDAR_TOKEN_JSON_B64` | no | none | Minted Gmail/Calendar tokens as base64, so they survive a deploy. The entrypoint restores them at startup. See [`docs/GOOGLE.md`](docs/GOOGLE.md). | -| `KNOWLEDGE_REPO_URL` / `KNOWLEDGE_GITHUB_TOKEN` | no | none | Set both to back the `knowledge` base with a Git repo instead of local files. Optional knobs: `KNOWLEDGE_BRANCH` (default `main`), `KNOWLEDGE_LOCAL_PATH`. | -| `DB_HOST` / `DB_PORT` / `DB_USER` / `DB_PASS` / `DB_DATABASE` | no | matches compose | Postgres connection. | -| `DB_DRIVER` | no | `postgresql+psycopg` | SQLAlchemy driver. | -| `AGNO_DEBUG` | no | `False` | If `True`, Agno emits verbose debug logs. Compose sets this for dev. | -| `WAIT_FOR_DB` | no | `False` | If `True`, the entrypoint blocks on the DB before starting. Compose sets this. | - -## Learn more - -- [`AGENTS.md`](AGENTS.md): architecture and conventions (source of truth for coding agents). -- [Agno documentation](https://docs.agno.com) -- [AgentOS introduction](https://docs.agno.com/agent-os/introduction) -- [Agno on GitHub](https://github.com/agno-agi/agno) (drop a star if this is useful). +- [`AGENTS.md`](AGENTS.md) — architecture and conventions (the source of truth for coding agents working on this repo). +- [`docs/`](docs/) — deep dives: [CRM](docs/CRM.md), [knowledge](docs/KNOWLEDGE.md), [MCP](docs/MCP.md), [Slack](docs/SLACK.md), [Google](docs/GOOGLE.md), [security](docs/SECURITY.md), [scaling](docs/SCALING.md), [the context network](docs/NETWORK.md). +- [Agno documentation](https://docs.agno.com) · [AgentOS introduction](https://docs.agno.com/agent-os/introduction) · [Agno on GitHub](https://github.com/agno-agi/agno) (drop a star if this is useful). diff --git a/docs/MCP.md b/docs/MCP.md index e36c0ab..8548e11 100644 --- a/docs/MCP.md +++ b/docs/MCP.md @@ -44,7 +44,7 @@ python scripts/connect.py --remove # undo It detects Claude Code, Codex, the Claude Desktop app, and Cursor and wires @context into each — running `claude mcp add` / `codex mcp add` for the CLIs, writing an `mcp-remote` bridge into `claude_desktop_config.json` for the desktop app, and a native `{url, headers}` entry into `~/.cursor/mcp.json` for Cursor (absolute `npx` path resolved where a bridge is used, existing keys preserved, a timestamped backup made, anything already configured skipped). For Claude Code it also **always-allows** the `use_context` tool (adds `mcp__context__use_context` to `permissions.allow` in `~/.claude/settings.json`) so the agent never prompts you before calling it — see [Claude Code (CLI)](#claude-code-cli) below. Pure stdlib, so no venv needed. Useful flags: `--clients claude-code codex claude-desktop cursor` to limit the set, `--url` for a non-default endpoint, `--config-path` to point at a non-standard desktop config. -**`--production`** targets your deployed instance: it reads `AGENTOS_URL` from `.env.production`, derives `https:///mcp`, and threads `Authorization: Bearer ` into every client for you. The JWT is read from `CONTEXT_MCP_JWT` in `.env.production`, else `--token `, else you're prompted — and you **self-issue** that token rather than copying one from os.agno.com (see [Self-issued production token](#self-issued-production-token) below). Claude Code gets the token via `--header`; Codex via `--bearer-token-env-var CONTEXT_JWT` (so it stays out of Codex's config — `export CONTEXT_JWT=` in your shell); Claude Desktop via the bridge's `--header`; Cursor via the `headers` block in `~/.cursor/mcp.json`. Switching a client from local to prod? CLI clients match by name, so re-run with `--force`. The full setup — minting the token, what lands where — is in [Self-issued production token](#self-issued-production-token) below; the [README](../README.md#connect-production-context-mcp-server) has the one-command quick-start. +**`--production`** targets your deployed instance: it reads `AGENTOS_URL` from `.env.production`, derives `https:///mcp`, and threads `Authorization: Bearer ` into every client for you. The JWT is read from `CONTEXT_MCP_JWT` in `.env.production`, else `--token `, else you're prompted — and you **self-issue** that token rather than copying one from os.agno.com (see [Self-issued production token](#self-issued-production-token) below). Claude Code gets the token via `--header`; Codex via `--bearer-token-env-var CONTEXT_JWT` (so it stays out of Codex's config — `export CONTEXT_JWT=` in your shell); Claude Desktop via the bridge's `--header`; Cursor via the `headers` block in `~/.cursor/mcp.json`. Switching a client from local to prod? CLI clients match by name, so re-run with `--force`. The full setup — minting the token, what lands where — is in [Self-issued production token](#self-issued-production-token) below; the [README](../README.md#interfaces) has the one-command quick-start. The per-client sections below are what it automates — reach for them to do it by hand, or to understand exactly what each form writes. @@ -157,7 +157,7 @@ ngrok http 8000 The deployed server is JWT-gated, so every client needs a bearer token. You **self-issue** it rather than copying one from os.agno.com — which means the whole flow is scriptable and the token is durable (a config-file token you set once, not a short-lived browser session token). -**Why self-issue.** AgentOS verifies a JWT against any public key it's configured to trust, and [`verification_keys` is a list](https://docs.agno.com/agent-os/security/authorization/self-hosted) — it tries each until one matches. The deployed app trusts **two** keys: the os.agno.com control-plane key (`JWT_VERIFICATION_KEY`, appended automatically by AgentOS, so the [AgentOS UI](../README.md#agentos-ui) keeps working) **and** a key you own (`CONTEXT_SELF_VERIFICATION_KEY`, wired in [`app/main.py`](../app/main.py)). Both issuers work at once. os.agno.com holds the private half of *its* key, so it mints the UI's tokens; you hold the private half of *yours*, so you mint your own MCP token. +**Why self-issue.** AgentOS verifies a JWT against any public key it's configured to trust, and [`verification_keys` is a list](https://docs.agno.com/agent-os/security/authorization/self-hosted) — it tries each until one matches. The deployed app trusts **two** keys: the os.agno.com control-plane key (`JWT_VERIFICATION_KEY`, appended automatically by AgentOS, so the [AgentOS UI](../README.md#interfaces) keeps working) **and** a key you own (`CONTEXT_SELF_VERIFICATION_KEY`, wired in [`app/main.py`](../app/main.py)). Both issuers work at once. os.agno.com holds the private half of *its* key, so it mints the UI's tokens; you hold the private half of *yours*, so you mint your own MCP token. **Mint it.** [`scripts/mint_mcp_jwt.py`](../scripts/mint_mcp_jwt.py) (run in the venv — it needs `pyjwt` + `cryptography`): diff --git a/docs/SLACK.md b/docs/SLACK.md index 3b0f767..8142fb5 100644 --- a/docs/SLACK.md +++ b/docs/SLACK.md @@ -11,7 +11,7 @@ The setup below also turns on two things beyond the chat interface: the `update_ ## Prerequisites -- @context running locally or in production (see [README#run-in-production](../README.md#run-in-production)) +- @context running locally or in production (see [README#deploy](../README.md#deploy)) - A Slack workspace where you can install @context - [ngrok](https://ngrok.com/download) installed and running if you are running @context locally [not needed for production] @@ -156,7 +156,7 @@ how are you? If you first set @context up against a local ngrok URL, your Slack app is still pointed at your local AgentOS — events stop reaching it the moment ngrok closes. To switch the app to your deployed instance, repoint the two request URLs at your AgentOS (Railway) domain: -1. Make sure @context is already deployed and serving in production (see [README#run-in-production](../README.md#run-in-production)). +1. Make sure @context is already deployed and serving in production (see [README#deploy](../README.md#deploy)). 2. Confirm `SLACK_BOT_TOKEN`, `SLACK_SIGNING_SECRET`, and your `OWNER_ID` (with your Slack email) are in `.env.production`, then run `./scripts/railway/env-sync.sh` so the deployed instance has them. 3. Go to [api.slack.com/apps](https://api.slack.com/apps) → your app. 4. **Event Subscriptions** → set **Request URL** to `https:///slack/events` and wait for the green **Verified**.