Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/docs/src/content/agents/mcp-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ The argument is your own text. The server puts it on one line, cuts it at 200 ch

The stdio server offers no prompts. Like the instructions, the prompts need a devframe patch that devframe 1.2.5 does not ship, so an installed package offers none until devframe ships it.

### Resource templates

The HTTP endpoint also lists a resource template for each page-scoped resource, such as `pangular://page/{pageId}/component-tree`, and sends `notifications/resources/list_changed` when a page starts reporting or expires. See [Read one page](./resources.md#read-one-page).

### Tool names

The server registers tools with a colon, as `pangular:get-routes`. MCP clients see them with an underscore, as `pangular_get-routes`. Calls with either form work.
Expand Down
31 changes: 31 additions & 0 deletions apps/docs/src/content/agents/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,37 @@ Every form the page reported (Signal Forms, reactive and template-driven), with

The active route tree (params, data, guards, resolvers) and recent navigations of each page. When the data is too large, the resource returns the URL and recent navigations of each page, and points to `inspect-route`, `explain-navigation` and `list-routes`.

## Read one page

The six resources above hold every connected page. When several tabs or apps are connected, read one page through its own resource instead.

### Page resource templates

The HTTP endpoint lists one *MCP resource template per resource in `resources/templates/list`. Put a page id from [`list-pages`](./tools.md#list-pages) in the URI:
Comment thread
erkamyaman marked this conversation as resolved.

| Template | Same data as |
| ----------------------------------------- | ------------------------- |
| `pangular://page/{pageId}/component-tree` | `pangular:component-tree` |
| `pangular://page/{pageId}/signal-graph` | `pangular:signal-graph` |
| `pangular://page/{pageId}/injector-tree` | `pangular:injector-tree` |
| `pangular://page/{pageId}/ngrx-store` | `pangular:ngrx-store` |
| `pangular://page/{pageId}/forms` | `pangular:forms` |
| `pangular://page/{pageId}/router` | `pangular:router` |

Each one returns the JSON of its resource with only that page in it, with the same `_untrusted` field and the same summary when it is too large. The selection made in the panel (`selectedId` and the like) is left out, since it belongs to no page. If no page with that id reports the data, the answer is a markdown note that lists the pages that do.

A template follows its resource: it is left out when the inspector is off through `inspectors` or `agent.tools`.

### Per-page entries in the list

Some clients don't read templates. For them, `resources/list` also names one resource per reporting page, such as `pangular://page/<pageId>/component-tree`.

### List changes

When a page starts reporting or expires, the server sends `notifications/resources/list_changed`, so a client can list the resources again. Changes settle for half a second first, and a report from a page that is already listed sends nothing.

Like the prompts, the templates need a devframe patch that devframe 1.2.5 does not ship. The stdio server has no templates, since no page connects to it.

## Shared state

The devtools keep their live data in shared-state keys. Every key is also listed as a resource, at `devframe://state/<key>` with the key encoded.
Expand Down
13 changes: 7 additions & 6 deletions apps/docs/src/content/contributing/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@ pnpm install

`patches/` holds `pnpm patch` changes to the built files of `devframe`, `@devframes/agentic` and `@devframes/hub` 1.2.5, listed under `patchedDependencies` in `pnpm-workspace.yaml`. They add what devframe doesn't ship:

| Patch | What it adds | Used by |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `instructions` | An MCP route option, sent as `instructions` in the `initialize` result. Patched in `devframe`, `@devframes/hub` and `@devframes/agentic`. | `pangularMcp()` in `packages/devtools/src/agent-mcp.ts` |
| `prompts` | An MCP route option with prompt templates. `@devframes/agentic` declares the `prompts` capability and answers `prompts/list` and `prompts/get` (invalid params for an unknown prompt or a missing required argument). Patched in `devframe`, `@devframes/hub` and `@devframes/agentic`. | `pangularMcpPrompts()` in `packages/devtools/src/agent-prompts.ts`, through `pangularMcp()` |
| `mcpContent` answers | `@devframes/agentic` sends a tool result's `mcpContent` blocks (`text` and `image`) as the MCP content, instead of the result as JSON. | `screenshot-component`, through `withMcpContent()` in `rpc/component-screenshots.ts` |
| Patch | What it adds | Used by |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `instructions` | An MCP route option, sent as `instructions` in the `initialize` result. Patched in `devframe`, `@devframes/hub` and `@devframes/agentic`. | `pangularMcp()` in `packages/devtools/src/agent-mcp.ts` |
| `prompts` | An MCP route option with prompt templates. `@devframes/agentic` declares the `prompts` capability and answers `prompts/list` and `prompts/get` (invalid params for an unknown prompt or a missing required argument). Patched in `devframe`, `@devframes/hub` and `@devframes/agentic`. | `pangularMcpPrompts()` in `packages/devtools/src/agent-prompts.ts`, through `pangularMcp()` |
| `resourceTemplates` | MCP route options for resource templates and a host hook for resource list changes. `@devframes/agentic` answers `resources/templates/list`, reads a URI through the first template that matches it (`{name}` variables only, one URI segment each), adds each template's `list()` entries to `resources/list`, and sends `notifications/resources/list_changed` when `onResourcesChanged` calls back. Patched in `devframe`, `@devframes/hub` and `@devframes/agentic`. | `pangularMcpResourceTemplates()` and `pangularMcpPagesChanged()` in `packages/devtools/src/agent-page-resources.ts`, through `pangularMcp()` |
| `mcpContent` answers | `@devframes/agentic` sends a tool result's `mcpContent` blocks (`text` and `image`) as the MCP content, instead of the result as JSON. | `screenshot-component`, through `withMcpContent()` in `rpc/component-screenshots.ts` |

The `exposeSharedState` filter, which hides the shared-state keys of an inspector with `agent.tools.<name>: false`, is part of devframe since 1.2.4 and needs no patch.

The patches apply only inside this repository. An app that installs the published package gets no server `instructions` and no prompts, and `screenshot-component` answers with its text note only, until devframe ships them. Each patch names an exact version and file names with build hashes, so a devframe upgrade needs the patches redone with `pnpm patch`, or removed once devframe has the feature.
The patches apply only inside this repository. An app that installs the published package gets no server `instructions`, no prompts and no page resource templates, and `screenshot-component` answers with its text note only, until devframe ships them. Each patch names an exact version and file names with build hashes, so a devframe upgrade needs the patches redone with `pnpm patch`, or removed once devframe has the feature.

## Project structure

Expand Down
2 changes: 1 addition & 1 deletion docs/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ An MCP prompt: a ready request for one workflow, such as `debug-error` or `fix-f
_Avoid_: template, command, skill (a skill is a `SKILL.md` folder the agent reads)

**Resource**:
Live state an agent reads as JSON over MCP, such as `pangular:component-tree`. It holds what the connected pages reported, so it is empty when no page is connected.
Live state an agent reads as JSON over MCP, such as `pangular:component-tree`. It holds what the connected pages reported, so it is empty when no page is connected. Each page-scoped resource also has a per-page resource template, `pangular://page/{pageId}/<name>`, that holds one page (`packages/devtools/src/agent-page-resources.ts`).
_Avoid_: snapshot, feed, state dump

**Source scan**:
Expand Down
13 changes: 13 additions & 0 deletions packages/devtools/src/__tests__/agent-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
type AgentEntryKind,
type PangularConfig,
} from '../config.ts';
import { PAGE_RESOURCE_NAMES } from '../agent-page-resources.ts';
import { createPangular, type PangularHostOptions } from '../devframe.ts';
import { travelServerPlugin } from '../../../../src/app/devtools/travel-plugin.server.ts';

Expand Down Expand Up @@ -112,6 +113,18 @@ describe('agent metadata', () => {
expect(Object.keys(AGENT_ENTRIES).filter((name) => !kinds.has(name))).toEqual(page);
});

// Per-page MCP resource templates are route options, not agent registrations,
// so they have no entry of their own: each one follows its page resource's entry.
it('maps each per-page resource template to a page resource entry', () => {
for (const name of PAGE_RESOURCE_NAMES) {
expect(AGENT_ENTRIES[name], name).toMatchObject({ kind: 'resource', page: true });
}
const pageResources = Object.keys(AGENT_ENTRIES).filter(
(name) => AGENT_ENTRIES[name]?.kind === 'resource' && AGENT_ENTRIES[name]?.page,
);
expect(sorted(pageResources)).toEqual(sorted(PAGE_RESOURCE_NAMES));
});

it('keeps each entry consistent', () => {
for (const [name, entry] of Object.entries(AGENT_ENTRIES)) {
expect(name, name).not.toMatch(/^(pangular|plugin):/);
Expand Down
Loading
Loading