Sources Configuration Guide
This guide explains how to configure sources (MCP servers, APIs, local filesystems) in Vorno.
CLI-first workflow (recommended): Use
vorno-cli source ...commands instead of editing source config files directly.
vorno-cli source --help- Canonical command reference: vorno-cli.md
Source Setup Process
Section titled “Source Setup Process”When a user wants to add a new source, follow this conversational setup process to create a tailored, well-documented integration.
0. Check for a Specialized Source Guide (REQUIRED FIRST STEP)
Section titled “0. Check for a Specialized Source Guide (REQUIRED FIRST STEP)”Before doing anything else, check for a service-specific guide in the sources/ directory next to this file. Read the index, or list the directory.
Available guides: GitHub, Linear, Slack, Craft, Gmail, Google Calendar, Google Drive, Google Docs, Google Sheets, Outlook, Microsoft Calendar, Teams, SharePoint, Filesystem, Brave Search, Memory
Google and Microsoft services also have shared prerequisite guides — google.md and microsoft.md — which the service guides link to. Read those too; Microsoft OAuth needs an environment variable that is not set by default, and the Google flow has a refresh-token expiry trap that surfaces a week after setup.
If a guide exists for the service:
- Read the whole guide before writing any config — the blocking prerequisites are not always at the top
- Follow any prerequisite it names before proceeding (e.g. GitHub works best when the
ghCLI is already installed and authenticated; Slack and Microsoft both have setup that must happen outside Vorno) - Use the narrowest scope that does the job. The guides list read-only and per-file alternatives to the broad defaults. Ask the user what they actually want to do, then scope the credential to that — not to their whole account
- Re-verify anything that fails. The guides record a verification date; if a console path or endpoint does not match what the user sees, trust the live service and check with WebSearch or the in-app browser
If no guide exists for the service, establish the following with your web tools before writing any config — your training data is not a reliable source for auth flows, and a source built from a stale assumption fails at credential time, which is the most annoying place to fail:
- Does an official MCP server exist? Prefer it over a hand-rolled REST source — it ships its own tool schemas and the vendor keeps it current
- Auth scheme and credential setup — where the user creates a token or OAuth app, and the narrowest scopes the task needs
- Prerequisites — some services expect a CLI, an admin approval, or an app registration first. Check before proceeding rather than failing halfway
Why this matters: Some services have important prerequisites or gotchas that MUST be checked before creating a source. Skipping this step can lead to failed setups or redundant configurations.
0.5. Choose Source vs Browser Path (RECOMMENDED PRE-FLIGHT)
Section titled “0.5. Choose Source vs Browser Path (RECOMMENDED PRE-FLIGHT)”Sources remain the default for reusable integrations. Before building a new source, ask: Is this repeatable integration work, or a one-off/UI-driven task?
Prefer creating/using a source when:
- The workflow is repeatable and likely to be reused
- Structured querying/reporting is needed
- Team consistency and automation matter
- API/MCP auth is stable enough to maintain
Prefer in-app browser first (or as fallback) when:
- Auth/setup is known to be brittle (common with some Gmail/Microsoft scenarios)
- The user needs a one-off task completed quickly
- The API is limited, unstable, or missing required operations
- The workflow is UI-first and not worth full integration overhead
If you choose browser-first, still offer optional source setup later when the user needs repeatability.
1. Understand User Intent
Section titled “1. Understand User Intent”Before creating any configuration, ask questions to understand:
- Primary purpose: What do they want to accomplish with this source?
- Scope: Specific projects, teams, repositories, or data to focus on?
- Common tasks: What operations will they perform most often?
- Access level: Read-only exploration or full access?
Example questions:
“I’d be happy to help set up Linear! A few questions:
- What will you primarily use Linear for? (issue tracking, sprint planning, etc.)
- Are there specific teams or projects you want to focus on?
- Should I set it up for read-only exploration or full access?“
2. Research the Service
Section titled “2. Research the Service”Use available tools to learn about the service:
- WebSearch/WebFetch: Find official documentation, API references, best practices
- In-app browser tools: Use when docs are dynamic, interactive, or behind login
- Look up: Rate limits, quotas, authentication methods
- Identify: Key endpoints or tools relevant to user’s stated goals
- Note: Any limitations or gotchas to document
Source vs Browser: Practical Examples
Section titled “Source vs Browser: Practical Examples”- Gmail/Microsoft setup keeps failing auth: attempt source setup, but confirm browser fallback for immediate task completion.
- Need a one-off export from an admin UI: use browser directly; skip full source setup unless recurring.
- API lacks the required endpoint but UI supports it: use browser as preferred path and document limitation.
3. Configure Intelligently
Section titled “3. Configure Intelligently”Based on research and user intent, create config.json with ALL required fields:
Core fields:
id- REQUIRED: Unique identifier string. Format:{slug}_{random}(e.g.,linear_a1b2c3d4). Generate the random part with any method (e.g., 8 hex chars).name,slug,provider,type- Basic identificationicon- RECOMMENDED: URL to the service’s favicon, logo, or app icon. The icon is auto-downloaded and cached locally. Use an emoji as fallback.tagline- RECOMMENDED: Short description for agent context (e.g., “Issue tracking, sprint planning, and project management”)- Type-specific config (
mcp,api, orlocal) - Authentication method appropriate for the service
4. Configure Explore Mode Permissions (REQUIRED)
Section titled “4. Configure Explore Mode Permissions (REQUIRED)”Sources should work in Explore mode by default. Create permissions.json to allow read-only operations.
How it works: Patterns in a source’s permissions.json are automatically scoped to that source. Write simple patterns like list - the system converts them to mcp__<sourceSlug>__.*list internally. This prevents cross-source leakage.
For MCP sources:
- After connecting, list the server’s available tools
- Identify read-only tools (list, get, search, find, query operations)
- Create simple patterns for those operations
{ "allowedMcpPatterns": [ { "pattern": "list", "comment": "All list operations" }, { "pattern": "get", "comment": "All get/read operations" }, { "pattern": "search", "comment": "All search operations" }, { "pattern": "find", "comment": "All find operations" } ]}For API sources:
{ "allowedApiEndpoints": [ { "method": "GET", "path": ".*", "comment": "All GET requests are read-only" }, { "method": "POST", "path": "^/search", "comment": "Search endpoint (read-only despite POST)" } ]}For local sources:
{ "allowedBashPatterns": [ { "pattern": "^(ls|cat|head|tail|grep|find|tree)\\s", "comment": "Read-only commands" } ]}Goal: Sources should be fully functional in Explore mode. Allow all read operations by default. Only block actual mutations (create, update, delete).
5. Write Comprehensive guide.md
Section titled “5. Write Comprehensive guide.md”Create a guide.md tailored to the user’s context:
- Summarize the source’s purpose in their specific use case
- Document capabilities relevant to their workflow
- Include specific project/team/scope references they mentioned
- Add usage examples tailored to their tasks
- Note rate limits, quotas, or limitations
6. Test and Validate (MANDATORY)
Section titled “6. Test and Validate (MANDATORY)”You MUST use the source_test tool after creating any source. This applies to ALL source types - MCP, API, and local filesystem sources. This is not optional.
mcp__session__source_test({ sourceSlug: "{slug}" })The source_test tool:
- Validates config.json against the schema
- Downloads and caches the icon if a URL was provided
- Tests the connection to verify the source is reachable
- Reports missing fields (icon, tagline) that should be added
- Auto-enables the source (default): on a clean run it flips
enabled: truein config if needed and activates the source in the current session so its tools become available without a restart. PassautoEnable: falseto keep pure validation behavior.
After validation passes, trigger the appropriate auth flow:
- OAuth sources:
source_oauth_trigger({ sourceSlug: "{slug}" }) - Bearer/API key:
source_credential_prompt({ sourceSlug: "{slug}", mode: "bearer" }) - Google services:
source_google_oauth_trigger({ sourceSlug: "{slug}" }) - Microsoft services:
source_microsoft_oauth_trigger({ sourceSlug: "{slug}" }) - Slack:
source_slack_oauth_trigger({ sourceSlug: "{slug}" })
Do NOT skip validation - it catches config errors before they cause runtime failures.
guide.md Best Practices
Section titled “guide.md Best Practices”The guide.md file is critical—it helps Claude understand how to use the source effectively in future sessions.
Structure
Section titled “Structure”# Source Name
Brief description of what this source provides and the user's specific use case.
## Scope
What data/functionality this provides. Include:- Specific projects, teams, or repos the user mentioned- Relevant filters or defaults- Any limitations on access
## Guidelines
- Best practices for using this source- Rate limits or quotas to be aware of- Common patterns the user will need- Things to avoid or be careful about
## Examples
Concrete examples tailored to the user's workflow:- "To find issues in the Craft iOS project: ..."- "To search for recent commits by the mobile team: ..."Key Principles
Section titled “Key Principles”- Be specific to user context: Reference the projects, teams, and tasks they mentioned
- Include concrete examples: Use their actual project names and workflows
- Document user preferences: Any preferences they shared for future sessions
- Note scope boundaries: What the source can and cannot do
- Keep it actionable: Focus on what Claude needs to know to help effectively
Example Conversational Flow
Section titled “Example Conversational Flow”User: I want to add Linear
Agent: [FIRST: Reads sources/linear.md]
Agent: I found the Linear setup guide! A few questions:1. What will you primarily use Linear for? (issue tracking, sprint planning, reporting?)2. Are there specific teams or projects you want to focus on?3. Should I set it up for read-only exploration or full access? (Linear has a dedicated read-only endpoint — if you want read-only, I'll use that rather than relying on permission rules alone.)
User: Issue tracking for my iOS team, project called "Craft iOS"
Agent: [Uses WebSearch to verify current Linear MCP URL]Agent: [Creates config.json with: - icon: (searches web for app icon or uses favicon) - tagline: "Issue tracking for the Craft iOS team" - OAuth authentication]Agent: [Writes guide.md mentioning "Craft iOS" project and iOS team context]Agent: [Sets up permissions.json for read-only listing in Explore mode]Agent: [MANDATORY: Runs source_test to validate] mcp__session__source_test({ sourceSlug: "linear" })Agent: [Triggers source_oauth_trigger for authentication] mcp__session__source_oauth_trigger({ sourceSlug: "linear" })
Agent: Linear is now set up! I've configured it to focus on the "Craft iOS" project.You can use it to:- Search and view issues in Craft iOS- Track sprint progress for the iOS team- Create and update issues
Would you like me to show you what issues are currently open?Overview
Section titled “Overview”Sources are stored as folders under:
~/.craft-agent/workspaces/{workspaceId}/sources/{sourceSlug}/
Each source folder contains:
config.json- Source configuration (required)guide.md- Usage documentation for Claude (optional)permissions.json- Custom permission rules for Explore mode (optional)icon.svg,icon.png,icon.jpg, oricon.jpeg- Source icon (optional)
config.json Schema
Section titled “config.json Schema”{ "id": "linear_a1b2c3d4", // Unique identifier: {slug}_{random} "name": "Human-readable name", "slug": "url-safe-identifier", "enabled": true, "provider": "provider-name", "type": "mcp" | "api" | "local",
// RECOMMENDED: Icon and tagline for better UI and agent context "icon": "https://example.com/favicon.ico", // URL (auto-downloaded) or emoji "tagline": "Brief description for agent context",
// For MCP sources: "mcp": { "url": "https://mcp.example.com", "authType": "oauth" | "bearer" | "none" },
// For API sources: "api": { "baseUrl": "https://api.example.com/", // MUST have trailing slash "authType": "bearer" | "header" | "query" | "basic" | "oauth" | "none", "headerName": "X-API-Key", // For single header auth "headerNames": ["X-API-KEY", "X-APP-KEY"], // For multi-header auth (2+ headers) "queryParam": "api_key", // For query auth "authScheme": "Bearer" // For bearer auth (default: "Bearer") },
// For local sources: "local": { "path": "/path/to/folder" },
// Status (updated by source_test): "isAuthenticated": true, "connectionStatus": "connected" | "needs_auth" | "failed" | "untested", "lastTestedAt": 1704067200000,
// Icon: emoji or URL (auto-downloaded to local icon.* file) // Local icon files are auto-discovered, no config needed "icon": "🔧", // Emoji icon (optional)
// Timestamps: "createdAt": 1704067200000, "updatedAt": 1704067200000}Source Types
Section titled “Source Types”MCP Sources
Section titled “MCP Sources”Model Context Protocol servers provide tools via HTTP/SSE.
OAuth authentication (recommended):
{ "id": "linear_a1b2c3d4", "type": "mcp", "provider": "linear", "mcp": { "url": "https://mcp.linear.app", "authType": "oauth" }}After creating, use source_oauth_trigger to authenticate.
Bearer token authentication:
{ "type": "mcp", "provider": "custom-mcp", "mcp": { "url": "https://my-mcp-server.com", "authType": "bearer" }}After creating, use source_credential_prompt with mode “bearer”.
Public (no auth):
{ "type": "mcp", "provider": "public-mcp", "mcp": { "url": "https://public-mcp.example.com", "authType": "none" }}Stdio transport (local command):
For MCP servers that run locally via command line (npx, node, python), use the stdio transport.
Users often provide configs in Claude Desktop / Claude Code format:
{ "mcpServers": { "airbnb": { "command": "npx", "args": ["-y", "@openbnb/mcp-server-airbnb"] } }}Convert to native format:
{ "type": "mcp", "name": "Airbnb", "provider": "airbnb", "mcp": { "transport": "stdio", "command": "npx", "args": ["-y", "@openbnb/mcp-server-airbnb"], "authType": "none" }}With environment variables:
{ "type": "mcp", "name": "Brave Search", "provider": "brave", "mcp": { "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-brave-search"], "env": { "BRAVE_API_KEY": "your-api-key" }, "authType": "none" }}API Sources
Section titled “API Sources”REST APIs become flexible tools that Claude can call.
Request bodies: By default, params is JSON-serialized for POST/PUT/PATCH requests. For endpoints that expect non-JSON bodies (plain text, XML, form data, etc.), use the special _rawBody and _contentType params:
{ "params": { "_rawBody": "raw string content to send as-is", "_contentType": "text/plain" }}_rawBody(string) — sent as the request body without JSON encoding_contentType(string, optional) — sets the Content-Type header (defaults totext/plain)
IMPORTANT: Authenticated API sources require a testEndpoint to validate credentials during source_test. Without it, we cannot verify your credentials work.
Header authentication (X-API-Key style):
{ "type": "api", "provider": "exa", "api": { "baseUrl": "https://api.exa.ai/", "authType": "header", "headerName": "x-api-key", "testEndpoint": { "method": "POST", "path": "search", "body": { "query": "test", "numResults": 1 } } }}Bearer token (Authorization header):
{ "type": "api", "provider": "openai", "api": { "baseUrl": "https://api.openai.com/v1/", "authType": "bearer", "testEndpoint": { "method": "GET", "path": "models" } }}Query parameter:
{ "type": "api", "provider": "weather", "api": { "baseUrl": "https://api.weather.com/", "authType": "query", "queryParam": "apikey", "testEndpoint": { "method": "GET", "path": "v1/current" } }}Basic authentication:
{ "type": "api", "provider": "jira", "api": { "baseUrl": "https://your-domain.atlassian.net/rest/api/3/", "authType": "basic", "testEndpoint": { "method": "GET", "path": "myself" } }}Multi-header authentication:
Some APIs require multiple authentication headers simultaneously. For example, Datadog requires both DD-API-KEY and DD-APPLICATION-KEY. Use the headerNames array to specify all required headers:
{ "type": "api", "provider": "datadog", "api": { "baseUrl": "https://api.datadoghq.com/api/", "authType": "header", "headerNames": ["DD-API-KEY", "DD-APPLICATION-KEY"], "testEndpoint": { "method": "GET", "path": "v1/validate" } }}When headerNames is specified:
- Each header name gets its own input field during authentication
- All header values are stored together as a JSON object
- Each header is added to every API request
To prompt for multi-header credentials:
source_credential_prompt({ sourceSlug: "datadog", mode: "multi-header", headerNames: ["DD-API-KEY", "DD-APPLICATION-KEY"], description: "Enter your Datadog API credentials"})Common multi-header use cases:
- Datadog:
DD-API-KEY+DD-APPLICATION-KEY - APIs with identity + signing keys: Separate API key and secret
- Services with app + user credentials: Application key plus user token
Generic OAuth (authType: ‘oauth’):
For API sources that use OAuth 2.0 but aren’t Google, Slack, or Microsoft. Two modes:
Auto-discovery (recommended): If the API supports RFC 9728 (OAuth Protected Resource Metadata), just set authType: "oauth" — endpoints and client registration are discovered automatically:
{ "name": "Craft Connect", "type": "api", "provider": "craft", "api": { "baseUrl": "https://connect.craft.do/my/api/v1/", "authType": "oauth" }}Explicit config: For APIs without standard OAuth metadata (GitHub, Linear, etc.), provide endpoints manually:
{ "name": "GitHub", "type": "api", "provider": "github", "api": { "baseUrl": "https://api.github.com/", "authType": "oauth", "oauth": { "authorizationUrl": "https://github.com/login/oauth/authorize", "tokenUrl": "https://github.com/login/oauth/access_token", "clientId": "Iv1.your_client_id", "clientSecret": "your_client_secret", "scopes": ["repo", "read:user"] } }}The oauth block fields (only needed for explicit config):
authorizationUrl(required): The OAuth authorization endpointtokenUrl(required): The OAuth token exchange endpointclientId(required): Your OAuth app’s client IDclientSecret(optional): Client secret — not required for public PKCE clientsscopes(optional): Requested OAuth scopesaudience(optional): Auth0-style audience parameterextraParams(optional): Additional query params for the authorization URL (e.g.{"access_type": "offline"})
To trigger OAuth authentication, use source_oauth_trigger (the same tool used for MCP OAuth):
source_oauth_trigger({ sourceSlug: "github" })Tokens are sent as Authorization: Bearer {token} on every request. Token refresh is automatic when a refresh token is available.
Basic auth with optional password:
Some APIs use HTTP Basic Auth but only require the username field (API key), leaving the password empty. For these APIs, use passwordRequired: false when prompting for credentials:
source_credential_prompt({ sourceSlug: "ashby", mode: "basic", passwordRequired: false, // Password field becomes optional labels: { username: "API Key" }, description: "Enter your Ashby API key"})When passwordRequired: false:
- The password field shows “(optional)” label and “Optional - leave blank” placeholder
- The Save button enables with just a username
- Empty string is submitted for password (per HTTP Basic Auth spec:
base64(username:))
Note: passwordRequired only applies to mode: "basic". It defaults to true for backward compatibility with services like Jira or Amplitude that require both username and password.
testEndpoint Configuration
Section titled “testEndpoint Configuration”The testEndpoint specifies which endpoint to call when validating credentials:
{ "testEndpoint": { "method": "GET", // "GET" or "POST" "path": "v1/me", // Path relative to baseUrl (NO leading slash) "body": { ... } // Optional: request body for POST }}IMPORTANT URL formatting:
baseUrlMUST have a trailing slash:https://api.example.com/v1/testEndpoint.pathmust NOT have a leading slash:users/me
Choose an endpoint that:
- Requires authentication (to verify credentials work)
- Is lightweight (doesn’t fetch much data)
- Returns quickly (health/status endpoints are ideal)
Common patterns:
me,user,profile- User info endpointsv1/status,health- Status endpoints that require authmodels,projects- List endpoints with minimal data
Public APIs (authType: ‘none’) don’t require testEndpoint - we test by hitting the base URL.
renewEndpoint Configuration (Optional)
Section titled “renewEndpoint Configuration (Optional)”The optional renewEndpoint enables automatic token renewal for non-OAuth bearer-token APIs. When the token expires, the system calls this endpoint to get a fresh token — no manual re-authentication needed.
{ "api": { "baseUrl": "https://api.example.com/", "authType": "bearer", "renewEndpoint": { "path": "auth/refresh", "method": "POST", "tokenField": "access_token", "expiresInField": "expires_in" } }}Fields:
| Field | Required | Default | Description |
|---|---|---|---|
path |
Yes | — | Renew URL — relative path (resolved against baseUrl) or absolute URL |
method |
No | "POST" |
HTTP method: "GET" or "POST" |
body |
No | — | Request body (JSON). Use {{token}} as placeholder for the current access token |
headers |
No | — | Extra headers. Use {{token}} as placeholder. Merged on top of defaultHeaders |
tokenField |
No | "access_token" |
JSON field name for the new token in the response |
expiresInField |
No | "expires_in" |
JSON field name for expiry in seconds in the response |
fallbackTtlSecs |
No | — | Fallback TTL in seconds when the response doesn’t include expiry info |
How it works:
- Before each API request, the system checks if the token is expired or expiring soon (within 5 minutes)
- If so, it calls the
renewEndpointwith the current token in the Authorization header - The new token and expiry are extracted from the response and stored
- The refreshed token is used for the API request
Token substitution: Use {{token}} in body or headers to insert the current access token. This supports nested objects — all string values are scanned recursively.
Example with token in request body:
{ "renewEndpoint": { "path": "auth/refresh", "body": { "current_token": "{{token}}" }, "tokenField": "new_token", "expiresInField": "ttl" }}When body is omitted, the current token is sent via the standard Authorization header (using the source’s authScheme).
Note: This is for APIs with their own token renewal mechanism, not OAuth. For OAuth-based APIs, use authType: "oauth" instead.
Local Sources
Section titled “Local Sources”Filesystem access for local folders.
{ "type": "local", "provider": "obsidian", "local": { "path": "/Users/me/Documents/ObsidianVault" }}After creating, run source_test to validate the path exists and is accessible.
guide.md Format
Section titled “guide.md Format”The guide.md file helps Claude understand how to use the source effectively.
# Source Name
Brief description of what this source provides.
## Scope
What data/functionality this source provides access to.
## Guidelines
- Best practices for using this source- Rate limits or quotas to be aware of- Common patterns and examples
## API Reference
For API sources, document the available endpoints:
### POST /searchSearch for content.
**Parameters:**- `query` (string, required): Search query- `limit` (number, optional): Max results (default: 10)
**Example:**\`\`\`json{ "query": "machine learning", "limit": 5}\`\`\`permissions.json Format
Section titled “permissions.json Format”Custom rules to extend Explore mode permissions for this source.
{ "allowedMcpPatterns": [ { "pattern": "^mcp__linear__list", "comment": "Allow listing resources in Explore mode" } ], "allowedApiEndpoints": [ { "method": "GET", "path": "^/search", "comment": "Allow search endpoint in Explore mode" }, { "method": "POST", "path": "^/v1/query$", "comment": "POST allowed for query-only endpoints" } ], "allowedBashPatterns": [ { "pattern": "^ls\\s", "comment": "Allow ls commands" } ]}Icon Handling
Section titled “Icon Handling”The config.icon field controls the source icon. Resolution follows this priority:
config.icon value |
Behavior |
|---|---|
Emoji (e.g., "🔧") |
Rendered as emoji text |
Local path "./icon.svg" |
Loads from sources/{slug}/icon.svg |
URL "https://..." |
Auto-downloaded to local icon.* file by source_test |
| Undefined/null | Auto-discovers sources/{slug}/icon.{svg,png}, falls back to favicon |
Examples:
// Emoji icon{ "icon": "📝" }
// Explicit local path (rarely needed - auto-discovery handles this){ "icon": "./icon.svg" }
// URL (downloaded automatically by source_test){ "icon": "https://linear.app/static/favicon.svg" }
// No icon field - auto-discovers icon.svg/icon.png or resolves favicon{}Best practice: Set icon to a URL when creating a source. Run source_test to download and cache it locally. The app then uses the local file for fast, offline-capable display.
Provider Domain Cache
Section titled “Provider Domain Cache”For favicon resolution, a cache maps provider names to their canonical domains at:
~/.craft-agent/provider-domains.json
Format:
{ "version": 1, "domains": { "linear": "linear.app", "notion": "notion.so", "brave": "brave.com" }, "updatedAt": 1704067200000}When to update: If a source’s favicon appears incorrect (generic globe, wrong icon), add the provider→domain mapping to this file. The app loads this cache on startup.
Example: If “acme-mcp” source shows wrong icon, add:
"acme": "acme.com"Common Providers
Section titled “Common Providers”Quick reference only. The per-service guides in sources/ are authoritative — they cover scopes, prerequisites, and gotchas this table cannot.
Gmail (and other Google services)
Section titled “Gmail (and other Google services)”Provider: google, Type: api
Requires user-provided OAuth credentials in the source config:
googleOAuthClientId: Your Google OAuth Client IDgoogleOAuthClientSecret: Your Google OAuth Client Secret
Create credentials at Google Cloud Console as a Web application client (not “Desktop app”), and add https://thecraftagents.com/auth/callback as an authorized redirect URI. A client secret is required.
Uses OAuth via source_google_oauth_trigger. → google.md
Microsoft services (Outlook, Calendar, Teams, SharePoint)
Section titled “Microsoft services (Outlook, Calendar, Teams, SharePoint)”Provider: microsoft, Type: api, Base URL: https://graph.microsoft.com/v1.0/
Requires the MICROSOFT_OAUTH_CLIENT_ID environment variable — there is no config field for it. → microsoft.md
Linear
Section titled “Linear”Provider: linear, Type: mcp
URL: https://mcp.linear.app/mcp (include the /mcp path; /sse is deprecated), OAuth auth.
Read-only variant: https://mcp.linear.app/mcp/readonly. → linear.md
GitHub
Section titled “GitHub”Provider: github, Type: mcp
URL: https://api.githubcopilot.com/mcp/, bearer auth (PAT required - OAuth will fail).
Read-only variant: https://api.githubcopilot.com/mcp/readonly. → github.md
Provider: slack, Type: mcp
Official server: https://mcp.slack.com/mcp, OAuth. Requires an admin-approved Slack app.
The built-in source_slack_oauth_trigger needs SLACK_OAUTH_CLIENT_ID / SLACK_OAUTH_CLIENT_SECRET env vars. → slack.md
Exa (Search)
Section titled “Exa (Search)”Provider: exa, Type: api
Base URL: https://api.exa.ai, header auth with x-api-key.
Brave Search
Section titled “Brave Search”Provider: brave, Type: mcp
Transport: stdio, Command: npx -y @brave/brave-search-mcp-server --transport stdio, requires BRAVE_API_KEY env.
Not @modelcontextprotocol/server-brave-search — that reference package was retired. → brave-search.md
Memory
Section titled “Memory”Provider: memory, Type: mcp
Transport: stdio, Command: npx -y @modelcontextprotocol/server-memory, no auth.
Set MEMORY_FILE_PATH or the graph lives in the npx cache. → memory.md
Workflow
Section titled “Workflow”Creating a Source
Section titled “Creating a Source”Always follow the conversational setup process (see above). The key steps:
- Ask before creating: Understand user intent, scope, and common tasks
- Choose the right path: Confirm if this should be a source or a browser-first one-off workflow
- Research before configuring: Use WebSearch/WebFetch and browser tools as needed
- Tailor guide.md to context: Include specific projects/teams the user mentioned
- Test before declaring done: Validate config, trigger auth, verify connection
Technical steps:
-
Create the source folder:
Terminal window mkdir -p ~/.craft-agent/workspaces/{ws}/sources/my-source -
Write
config.jsonwith appropriate settings (see schemas above) -
Write
guide.mdtailored to user’s context and use case -
Create
permissions.jsonfor Explore mode - List the source’s tools, identify read-only operations (list, get, search), and add simple patterns. Patterns are auto-scoped to this source. -
Run
source_testto validate configuration and test connection -
If auth is required, trigger the appropriate flow:
source_oauth_triggerfor MCP OAuthsource_google_oauth_triggerfor Google services (Gmail, Calendar, Drive, Docs, Sheets, YouTube, Search Console)source_microsoft_oauth_triggerfor Microsoft servicessource_slack_oauth_triggerfor Slacksource_credential_promptfor API keys/tokens- For basic auth with optional password:
source_credential_prompt({ mode: "basic", passwordRequired: false })
-
Confirm with user that the source is working as expected
Testing a Source
Section titled “Testing a Source”Use source_test with the source slug:
- Validates config.json schema
- Tests connectivity
- Downloads icon if needed
- Updates connectionStatus
Troubleshooting
Section titled “Troubleshooting”“needs_auth” status:
- Source requires authentication
- Use appropriate auth trigger tool
“failed” status:
- Check
connectionErrorin config.json - Verify URL is correct
- Check network connectivity
Icon not showing:
- Ensure iconUrl is valid
- Run
source_testto re-download - Check file exists in source folder