Resources and Events
MCP resources exposed by the server and Inngest events emitted by tools for background processing.
Beyond tools, the MCP server exposes resources (read-only data the client can access) and certain tools emit events to trigger background processing via Inngest.
MCP Resources
The MCP specification defines resources as read-only data sources that clients can retrieve. Trovella registers one resource.
trovella://user/profile
Returns the authenticated user's identity as JSON. Registered in packages/mcp/src/resources/profile.ts.
URI: trovella://user/profile
MIME type: application/json
Response:
{
"id": "usr_abc123",
"name": "Kyle Olson",
"email": "kyle@trovella.ai"
}
The data comes directly from the McpAuthContext populated during PAT authentication -- no database query is required. This resource is useful for MCP clients that want to confirm the authenticated identity without calling the ping tool.
Adding a New Resource
New resources are registered in packages/mcp/src/server.ts alongside tools:
server.registerResource(
"Display Name",
"trovella://category/resource-name",
{ description: "...", mimeType: "application/json" },
async () => ({
contents: [
{
uri: "trovella://category/resource-name",
mimeType: "application/json",
text: JSON.stringify(data),
},
],
}),
);
Resources do not go through withToolCallLogging -- the middleware only wraps tool registrations. If a resource needs tenant-scoped data, the callback should call resolveOrganizationId() and withTenantContext() directly, following the same pattern as tool callbacks.
Inngest Event Emission
Two MCP tools emit events to trigger background processing via Inngest. The event emitter lives in packages/mcp/src/events.ts and the consumer functions live in apps/web/src/inngest/functions/. This separation means MCP tools are unaware of what processes their events.
Event: search/content.created
Triggers hybrid search indexing (chunking, contextual prefix generation, embedding, dual-write to pgvector and Typesense).
Emitted by:
store_research-- whencontentTextis providedstore_research_output-- whencontentSummaryis provided
Event data:
| Field | Type | Description |
|---|---|---|
sourceTable | enum | research_artifact, research_output, or extraction_result |
sourceId | string | UUID of the stored record |
title | string | Record title |
content | string | Text content for indexing |
organizationId | string | Tenant ID |
userId | string | Creator's user ID |
artifactType | string | (optional) Artifact type, for research_artifact |
mediaType | string | (optional) Media type, for research_output |
planId | string | (optional) Associated plan ID |
Graceful Degradation
The emitContentCreated() function follows a no-op pattern when Inngest is not configured:
function getInngest(): Inngest | null {
if (!process.env["INNGEST_BASE_URL"] && !process.env["INNGEST_DEV"]) {
return null; // No Inngest? No-op.
}
// Lazy initialization on first use
if (!inngest) {
inngest = new Inngest({ id: "trovella" });
}
return inngest;
}
export async function emitContentCreated(data: { ... }): Promise<void> {
const client = getInngest();
if (!client) return; // Silent no-op
await client.send({ name: "search/content.created", data });
}
This means:
- Database seeding works without Inngest running (the emitter returns silently)
- Tests do not need to mock Inngest (unless testing the event emission itself)
- Local development works with or without the Inngest dev server
Event Flow Diagram
store_research() store_research_output()
| |
v v
contentText? contentSummary?
| |
v v
emitContentCreated() emitContentCreated()
| |
+--------+ +--------------+
| |
v v
Inngest event queue
|
v
index-content function (4 durable steps)
Step 1: chunk-content
Step 2: generate-context
Step 3: embed-chunks
Step 4: store-and-sync
For the full event flow, consumer function details, and the retry/concurrency model, see Data & Storage -- Event Patterns.
Tools That Do Not Emit Events
Most MCP tools interact only with the database and do not emit background events. Notably, extract_data writes to extraction_result but does not currently emit search/content.created. This means extraction results are stored but not indexed for hybrid search. This is intentional for MVP -- extraction results are structured data (JSON) rather than prose, and the search pipeline is optimized for text content.
Related Pages
- Tool Protocol Overview -- server architecture and request lifecycle
- Authentication -- how
McpAuthContextis populated - Data & Storage -- Event Patterns -- detailed event flow, naming convention, and consumer design
- Data & Storage -- Background Jobs -- Inngest architecture, the index-content function
- Search & Retrieval -- Indexing -- the indexing pipeline that consumes these events
Middleware and Logging
How tool call logging middleware wraps every MCP tool invocation, the metadata capture strategy, and the audit log requirement.
Reasoning Overview
Architecture of the @repo/ai wrapper package -- the single entry point for all LLM operations, with automatic usage tracking and multi-provider support.