From 29f8b87ec778b448ac9cefe69470c993c44648d9 Mon Sep 17 00:00:00 2001 From: Holger Drewes <931137+holgerd77@users.noreply.github.com> Date: Tue, 6 Oct 2026 11:29:10 +0200 Subject: [PATCH 1/4] =?UTF-8?q?MCP=20docs=20launch=20polish=20=E2=80=94=20?= =?UTF-8?q?prompts,=20connect=20tabs,=20EIP=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- mcp-docs/.vitepress/config.ts | 9 + .../theme/__tests__/mcpConstants.spec.ts | 36 + .../theme/components/ClientTabs.vue | 145 +++ .../theme/components/CodeSnippet.vue | 20 + .../theme/components/CollapsibleChangelog.vue | 22 + .../theme/components/CopyButton.vue | 27 + .../.vitepress/theme/components/DoorStrip.vue | 20 + .../.vitepress/theme/components/EipHeader.vue | 48 + .../theme/components/EndpointCard.vue | 18 + .../.vitepress/theme/components/IconGrid.vue | 54 + .../.vitepress/theme/components/IconNote.vue | 22 + .../theme/components/LaunchFacts.vue | 19 + .../theme/components/LeadQuestion.vue | 9 + .../theme/components/PromptCard.vue | 43 + .../theme/components/PromptList.vue | 25 + .../.vitepress/theme/components/SpecTable.vue | 33 + .../.vitepress/theme/components/Steps.vue | 26 + .../components/__tests__/ClientTabs.spec.ts | 40 + .../components/__tests__/EipHeader.spec.ts | 40 + .../components/__tests__/PromptList.spec.ts | 87 ++ .../theme/components/__tests__/misc.spec.ts | 84 ++ .../.vitepress/theme/composables/useCopy.ts | 28 + mcp-docs/.vitepress/theme/custom.css | 927 ++++++++++++++++++ mcp-docs/.vitepress/theme/icons.ts | 57 ++ mcp-docs/.vitepress/theme/index.ts | 34 +- mcp-docs/.vitepress/theme/mcpConstants.ts | 44 + mcp-docs/__tests__/eipPages.spec.ts | 30 + mcp-docs/__tests__/urlStability.spec.ts | 65 ++ mcp-docs/index.md | 53 +- mcp-docs/public/icons/amsterdam.svg | 1 + mcp-docs/public/icons/ask.svg | 1 + mcp-docs/public/icons/connect.svg | 1 + mcp-docs/public/icons/explore.svg | 1 + mcp-docs/use/capabilities.md | 116 ++- mcp-docs/use/connect.md | 106 +- mcp-docs/use/coverage.md | 4 +- mcp-docs/use/eips/eip-2780.md | 115 ++- mcp-docs/use/eips/eip-7708.md | 115 ++- mcp-docs/use/eips/eip-7778.md | 127 ++- mcp-docs/use/eips/eip-7843.md | 113 ++- mcp-docs/use/eips/eip-7883.md | 113 ++- mcp-docs/use/eips/eip-7928.md | 111 ++- mcp-docs/use/eips/eip-7951.md | 108 +- mcp-docs/use/eips/eip-7954.md | 110 ++- mcp-docs/use/eips/eip-7976.md | 111 ++- mcp-docs/use/eips/eip-8024.md | 120 ++- mcp-docs/use/eips/eip-8037.md | 125 ++- mcp-docs/use/eips/eip-8038.md | 126 ++- mcp-docs/use/forks/fusaka.md | 22 +- mcp-docs/use/forks/glamsterdam.md | 59 +- mcp-docs/use/forks/historical-forks.md | 19 +- mcp-docs/use/guarantees.md | 4 +- mcp-docs/use/introduction.md | 88 +- mcp-docs/use/pricing.md | 4 +- mcp-docs/use/tools/describe-capabilities.md | 14 +- mcp-docs/use/tools/generate-artifact.md | 14 +- mcp-docs/use/tools/inspect-artifact.md | 14 +- mcp-docs/use/tools/run-block.md | 14 +- mcp-docs/use/tools/run-bytecode.md | 14 +- mcp-docs/use/tools/run-transaction.md | 14 +- 60 files changed, 3450 insertions(+), 519 deletions(-) create mode 100644 mcp-docs/.vitepress/theme/__tests__/mcpConstants.spec.ts create mode 100644 mcp-docs/.vitepress/theme/components/ClientTabs.vue create mode 100644 mcp-docs/.vitepress/theme/components/CodeSnippet.vue create mode 100644 mcp-docs/.vitepress/theme/components/CollapsibleChangelog.vue create mode 100644 mcp-docs/.vitepress/theme/components/CopyButton.vue create mode 100644 mcp-docs/.vitepress/theme/components/DoorStrip.vue create mode 100644 mcp-docs/.vitepress/theme/components/EipHeader.vue create mode 100644 mcp-docs/.vitepress/theme/components/EndpointCard.vue create mode 100644 mcp-docs/.vitepress/theme/components/IconGrid.vue create mode 100644 mcp-docs/.vitepress/theme/components/IconNote.vue create mode 100644 mcp-docs/.vitepress/theme/components/LaunchFacts.vue create mode 100644 mcp-docs/.vitepress/theme/components/LeadQuestion.vue create mode 100644 mcp-docs/.vitepress/theme/components/PromptCard.vue create mode 100644 mcp-docs/.vitepress/theme/components/PromptList.vue create mode 100644 mcp-docs/.vitepress/theme/components/SpecTable.vue create mode 100644 mcp-docs/.vitepress/theme/components/Steps.vue create mode 100644 mcp-docs/.vitepress/theme/components/__tests__/ClientTabs.spec.ts create mode 100644 mcp-docs/.vitepress/theme/components/__tests__/EipHeader.spec.ts create mode 100644 mcp-docs/.vitepress/theme/components/__tests__/PromptList.spec.ts create mode 100644 mcp-docs/.vitepress/theme/components/__tests__/misc.spec.ts create mode 100644 mcp-docs/.vitepress/theme/composables/useCopy.ts create mode 100644 mcp-docs/.vitepress/theme/icons.ts create mode 100644 mcp-docs/.vitepress/theme/mcpConstants.ts create mode 100644 mcp-docs/__tests__/eipPages.spec.ts create mode 100644 mcp-docs/__tests__/urlStability.spec.ts create mode 100644 mcp-docs/public/icons/amsterdam.svg create mode 100644 mcp-docs/public/icons/ask.svg create mode 100644 mcp-docs/public/icons/connect.svg create mode 100644 mcp-docs/public/icons/explore.svg diff --git a/mcp-docs/.vitepress/config.ts b/mcp-docs/.vitepress/config.ts index 98e8c8d6..f8ad0b71 100644 --- a/mcp-docs/.vitepress/config.ts +++ b/mcp-docs/.vitepress/config.ts @@ -35,6 +35,7 @@ export default defineConfig({ description: MCP_DOCS_DESCRIPTION, /** README is contributor-facing only — keep it out of the built site + sitemap. */ srcExclude: ['README.md'], + lastUpdated: true, head: [ ['script', {}, 'document.documentElement.classList.add("fyp-site-mcp")'], ['meta', { name: 'twitter:card', content: 'summary_large_image' }], @@ -134,6 +135,14 @@ export default defineConfig({ search: { provider: 'local', }, + editLink: { + pattern: 'https://github.com/feelyourprotocol/website/edit/main/mcp-docs/:path', + text: 'Edit this page on GitHub', + }, + docFooter: { + prev: 'Previous', + next: 'Next', + }, footer: { message: 'Get started = connect and ask questions. Reference = catalogues and tool schemas. Internals = for builders.', diff --git a/mcp-docs/.vitepress/theme/__tests__/mcpConstants.spec.ts b/mcp-docs/.vitepress/theme/__tests__/mcpConstants.spec.ts new file mode 100644 index 00000000..8c2534e9 --- /dev/null +++ b/mcp-docs/.vitepress/theme/__tests__/mcpConstants.spec.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest' + +import { + CLAUDE_CODE_ADD_COMMAND, + CODEX_ADD_COMMAND, + CODEX_CONFIG_TOML, + CURSOR_INSTALL_LINK, + cursorMcpInstallLink, + FYP_MCP_URL, +} from '../mcpConstants' + +describe('mcpConstants', () => { + it('builds a Cursor install deeplink whose config decodes to the server entry', () => { + const link = cursorMcpInstallLink('feel-your-protocol', { url: FYP_MCP_URL }) + expect(link.startsWith('cursor://anysphere.cursor-deeplink/mcp/install?')).toBe(true) + const params = new URL(link.replace('cursor://', 'https://')).searchParams + expect(params.get('name')).toBe('feel-your-protocol') + const decoded = JSON.parse(Buffer.from(params.get('config') ?? '', 'base64').toString('utf8')) + expect(decoded).toEqual({ url: FYP_MCP_URL }) + expect(CURSOR_INSTALL_LINK).toBe(link) + }) + + it('encodes unusual server names safely', () => { + const link = cursorMcpInstallLink('a b&c', { url: FYP_MCP_URL }) + expect(link).toContain('name=a%20b%26c') + }) + + it('uses the documented Claude Code and Codex forms', () => { + expect(CLAUDE_CODE_ADD_COMMAND).toBe( + `claude mcp add --transport http feel-your-protocol ${FYP_MCP_URL}`, + ) + expect(CODEX_ADD_COMMAND).toBe(`codex mcp add feel-your-protocol --url ${FYP_MCP_URL}`) + expect(CODEX_CONFIG_TOML).toContain('[mcp_servers.feel-your-protocol]') + expect(CODEX_CONFIG_TOML).toContain(`url = "${FYP_MCP_URL}"`) + }) +}) diff --git a/mcp-docs/.vitepress/theme/components/ClientTabs.vue b/mcp-docs/.vitepress/theme/components/ClientTabs.vue new file mode 100644 index 00000000..f7bbfd3e --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/ClientTabs.vue @@ -0,0 +1,145 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/CodeSnippet.vue b/mcp-docs/.vitepress/theme/components/CodeSnippet.vue new file mode 100644 index 00000000..b7a09c48 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/CodeSnippet.vue @@ -0,0 +1,20 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/CollapsibleChangelog.vue b/mcp-docs/.vitepress/theme/components/CollapsibleChangelog.vue new file mode 100644 index 00000000..2bb6c1cf --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/CollapsibleChangelog.vue @@ -0,0 +1,22 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/CopyButton.vue b/mcp-docs/.vitepress/theme/components/CopyButton.vue new file mode 100644 index 00000000..b02ef5db --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/CopyButton.vue @@ -0,0 +1,27 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/DoorStrip.vue b/mcp-docs/.vitepress/theme/components/DoorStrip.vue new file mode 100644 index 00000000..2f346687 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/DoorStrip.vue @@ -0,0 +1,20 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/EipHeader.vue b/mcp-docs/.vitepress/theme/components/EipHeader.vue new file mode 100644 index 00000000..a0d269ce --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/EipHeader.vue @@ -0,0 +1,48 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/EndpointCard.vue b/mcp-docs/.vitepress/theme/components/EndpointCard.vue new file mode 100644 index 00000000..04087027 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/EndpointCard.vue @@ -0,0 +1,18 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/IconGrid.vue b/mcp-docs/.vitepress/theme/components/IconGrid.vue new file mode 100644 index 00000000..dd250f77 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/IconGrid.vue @@ -0,0 +1,54 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/IconNote.vue b/mcp-docs/.vitepress/theme/components/IconNote.vue new file mode 100644 index 00000000..c87e1e59 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/IconNote.vue @@ -0,0 +1,22 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/LaunchFacts.vue b/mcp-docs/.vitepress/theme/components/LaunchFacts.vue new file mode 100644 index 00000000..8792624c --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/LaunchFacts.vue @@ -0,0 +1,19 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/LeadQuestion.vue b/mcp-docs/.vitepress/theme/components/LeadQuestion.vue new file mode 100644 index 00000000..193f1795 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/LeadQuestion.vue @@ -0,0 +1,9 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/PromptCard.vue b/mcp-docs/.vitepress/theme/components/PromptCard.vue new file mode 100644 index 00000000..c4ba56a6 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/PromptCard.vue @@ -0,0 +1,43 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/PromptList.vue b/mcp-docs/.vitepress/theme/components/PromptList.vue new file mode 100644 index 00000000..7a4a1ba1 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/PromptList.vue @@ -0,0 +1,25 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/SpecTable.vue b/mcp-docs/.vitepress/theme/components/SpecTable.vue new file mode 100644 index 00000000..185affea --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/SpecTable.vue @@ -0,0 +1,33 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/Steps.vue b/mcp-docs/.vitepress/theme/components/Steps.vue new file mode 100644 index 00000000..cde8660d --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/Steps.vue @@ -0,0 +1,26 @@ + + + diff --git a/mcp-docs/.vitepress/theme/components/__tests__/ClientTabs.spec.ts b/mcp-docs/.vitepress/theme/components/__tests__/ClientTabs.spec.ts new file mode 100644 index 00000000..ec5349de --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/__tests__/ClientTabs.spec.ts @@ -0,0 +1,40 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' + +import { + CLAUDE_CODE_ADD_COMMAND, + CODEX_ADD_COMMAND, + CURSOR_INSTALL_LINK, + FYP_MCP_URL, +} from '../../mcpConstants' +import ClientTabs from '../ClientTabs.vue' + +describe('ClientTabs', () => { + it('shows Cursor first with the install deeplink', () => { + const wrapper = mount(ClientTabs) + expect(wrapper.get('#fyp-panel-cursor').isVisible()).toBe(true) + expect(wrapper.get('#fyp-panel-claude').isVisible()).toBe(false) + expect(wrapper.get('a.fyp-install-link').attributes('href')).toBe(CURSOR_INSTALL_LINK) + }) + + it('switches tabs and shows verified commands per client', async () => { + const wrapper = mount(ClientTabs) + + await wrapper.get('#fyp-tab-claude').trigger('click') + expect(wrapper.get('#fyp-panel-claude').isVisible()).toBe(true) + expect(wrapper.get('#fyp-panel-claude').text()).toContain(CLAUDE_CODE_ADD_COMMAND) + + await wrapper.get('#fyp-tab-codex').trigger('click') + expect(wrapper.get('#fyp-panel-codex').text()).toContain(CODEX_ADD_COMMAND) + + await wrapper.get('#fyp-tab-other').trigger('click') + expect(wrapper.get('#fyp-panel-other').text()).toContain(FYP_MCP_URL) + }) + + it('never suggests a url entry in claude_desktop_config.json', () => { + const wrapper = mount(ClientTabs) + const claude = wrapper.get('#fyp-panel-claude').text() + expect(claude).toContain('custom connector') + expect(claude).not.toContain('"type": "http"') + }) +}) diff --git a/mcp-docs/.vitepress/theme/components/__tests__/EipHeader.spec.ts b/mcp-docs/.vitepress/theme/components/__tests__/EipHeader.spec.ts new file mode 100644 index 00000000..36964a84 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/__tests__/EipHeader.spec.ts @@ -0,0 +1,40 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' + +import EipHeader from '../EipHeader.vue' + +describe('EipHeader', () => { + it('renders number, title, status, tool links, and the exploration link', () => { + const wrapper = mount(EipHeader, { + props: { + number: 8037, + title: 'State creation gas', + status: 'Runnable', + fork: 'Amsterdam (Glamsterdam)', + tools: ['run_transaction', 'run_bytecode'], + launchNote: 'At public launch: runnable.', + explorationHref: 'https://feelyourprotocol.org/eip-8037-state-creation-gas', + }, + }) + + const text = wrapper.get('[data-testid="eip-header"]').text() + expect(text).toContain('EIP-8037') + expect(text).toContain('State creation gas') + expect(text).toContain('Runnable') + expect(text).toContain('At public launch: runnable.') + expect(wrapper.get('a[href="/use/tools/run-transaction"]').text()).toBe('run_transaction') + expect(wrapper.get('a[href="/use/tools/run-bytecode"]').exists()).toBe(true) + const twin = wrapper.get('a[href^="https://feelyourprotocol.org"]') + expect(twin.attributes('target')).toBe('_blank') + expect(twin.attributes('rel')).toContain('noopener') + }) + + it('renders an unknown tool as a plain chip instead of a broken link', () => { + const wrapper = mount(EipHeader, { + props: { number: '9999', title: 'Future', tools: ['not_a_tool'] }, + }) + const chip = wrapper.get('.fyp-chip--tool') + expect(chip.element.tagName).toBe('SPAN') + expect(chip.attributes('href')).toBeUndefined() + }) +}) diff --git a/mcp-docs/.vitepress/theme/components/__tests__/PromptList.spec.ts b/mcp-docs/.vitepress/theme/components/__tests__/PromptList.spec.ts new file mode 100644 index 00000000..f681a5d9 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/__tests__/PromptList.spec.ts @@ -0,0 +1,87 @@ +import { mount } from '@vue/test-utils' +import { afterEach, describe, expect, it, vi } from 'vitest' + +import PromptList from '../PromptList.vue' + +afterEach(() => { + vi.unstubAllGlobals() +}) + +describe('PromptList', () => { + it('renders intro, group labels, and every prompt', () => { + const wrapper = mount(PromptList, { + props: { + intro: 'Try these.', + groups: [ + { label: 'Start here', prompts: [{ text: 'Send 1 wei on Amsterdam.' }] }, + { label: 'Compare', prompts: [{ text: 'Fusaka vs Amsterdam.', fork: 'Fusaka vs Amsterdam' }] }, + ], + }, + }) + + expect(wrapper.get('[data-testid="prompt-list"]').text()).toContain('Try these.') + expect(wrapper.text()).toContain('Start here') + expect(wrapper.text()).toContain('Send 1 wei on Amsterdam.') + expect(wrapper.findAll('[data-testid="prompt-card"]')).toHaveLength(2) + expect(wrapper.find('.fyp-chip--fork').text()).toBe('Fusaka vs Amsterdam') + }) + + it('shows look-for hint, tool link, and related page link when given', () => { + const wrapper = mount(PromptList, { + props: { + groups: [ + { + label: 'Start here', + prompts: [ + { + text: 'Prompt.', + lookFor: 'about 21,000 vs 204,600', + tool: 'run_transaction', + toolHref: '/use/tools/run-transaction', + href: '/use/eips/eip-8037', + hrefLabel: 'EIP-8037', + }, + ], + }, + ], + }, + }) + + expect(wrapper.text()).toContain('about 21,000 vs 204,600') + expect(wrapper.get('a[href="/use/tools/run-transaction"]').text()).toBe('run_transaction') + expect(wrapper.get('a[href="/use/eips/eip-8037"]').text()).toContain('EIP-8037') + }) + + it('renders without groups (empty list does not crash)', () => { + const wrapper = mount(PromptList, { props: { groups: [] } }) + expect(wrapper.findAll('[data-testid="prompt-card"]')).toHaveLength(0) + }) + + it('copy button writes the prompt text and shows feedback', async () => { + const writeText = vi.fn().mockResolvedValue(undefined) + vi.stubGlobal('navigator', { clipboard: { writeText } }) + + const wrapper = mount(PromptList, { + props: { groups: [{ label: 'Start here', prompts: [{ text: 'Copy me.' }] }] }, + }) + + const button = wrapper.get('[data-testid="copy-button"]') + expect(button.text()).toBe('Copy') + await button.trigger('click') + expect(writeText).toHaveBeenCalledWith('Copy me.') + expect(button.text()).toBe('Copied') + }) + + it('stays usable when the clipboard API rejects', async () => { + const writeText = vi.fn().mockRejectedValue(new Error('denied')) + vi.stubGlobal('navigator', { clipboard: { writeText } }) + + const wrapper = mount(PromptList, { + props: { groups: [{ label: 'Start here', prompts: [{ text: 'Nope.' }] }] }, + }) + + const button = wrapper.get('[data-testid="copy-button"]') + await button.trigger('click') + expect(button.text()).toBe('Copy') + }) +}) diff --git a/mcp-docs/.vitepress/theme/components/__tests__/misc.spec.ts b/mcp-docs/.vitepress/theme/components/__tests__/misc.spec.ts new file mode 100644 index 00000000..2fa4dfe0 --- /dev/null +++ b/mcp-docs/.vitepress/theme/components/__tests__/misc.spec.ts @@ -0,0 +1,84 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' + +import CollapsibleChangelog from '../CollapsibleChangelog.vue' +import DoorStrip from '../DoorStrip.vue' +import IconGrid from '../IconGrid.vue' +import SpecTable from '../SpecTable.vue' +import Steps from '../Steps.vue' + +describe('SpecTable', () => { + it('renders linked and plain rows; external links open safely', () => { + const wrapper = mount(SpecTable, { + props: { + rows: [ + { label: 'Canonical spec', href: 'https://github.com/ethereum/EIPs', text: 'EIP on GitHub' }, + { label: 'Spec date', text: '2026-07-31' }, + { label: 'Coverage index', href: '/use/coverage', text: 'EIP catalogue' }, + ], + }, + }) + const links = wrapper.findAll('a') + expect(links[0].attributes('target')).toBe('_blank') + expect(links[1].attributes('target')).toBeUndefined() + expect(wrapper.text()).toContain('2026-07-31') + }) +}) + +describe('Steps', () => { + it('numbers steps and links titles when href is set', () => { + const wrapper = mount(Steps, { + props: { + steps: [ + { title: 'Discover', href: '/use/tools/describe-capabilities', detail: 'Probe.' }, + { title: 'Run' }, + ], + }, + }) + expect(wrapper.findAll('.fyp-steps__marker').map((n) => n.text())).toEqual(['1', '2']) + expect(wrapper.findAll('a')).toHaveLength(1) + }) +}) + +describe('IconGrid', () => { + it('renders a card for an unknown icon id without a glyph', () => { + const wrapper = mount(IconGrid, { + props: { items: [{ icon: 'missing' as 'book', title: 'Still here', detail: 'No glyph.' }] }, + }) + expect(wrapper.text()).toContain('Still here') + expect(wrapper.find('.fyp-icon-card__glyph').exists()).toBe(false) + }) + + it('links internal cards in the same tab and external ones in a new tab', () => { + const wrapper = mount(IconGrid, { + props: { + items: [ + { icon: 'cube', title: 'Internal', detail: 'x', href: '/use/eips/eip-7954' }, + { icon: 'book', title: 'External', detail: 'y', href: 'https://feelyourprotocol.org' }, + ], + }, + }) + const links = wrapper.findAll('a') + expect(links[0].attributes('target')).toBeUndefined() + expect(links[1].attributes('target')).toBe('_blank') + }) +}) + +describe('DoorStrip', () => { + it('renders one link per door', () => { + const wrapper = mount(DoorStrip, { + props: { items: [{ title: 'Get started', detail: 'Connect.', href: '/use/introduction' }] }, + }) + expect(wrapper.get('a').attributes('href')).toBe('/use/introduction') + }) +}) + +describe('CollapsibleChangelog', () => { + it('is closed by default and lists entries', () => { + const wrapper = mount(CollapsibleChangelog, { + props: { entries: [{ version: 'v0.1', summary: 'First.' }] }, + }) + expect(wrapper.get('details').attributes('open')).toBeUndefined() + expect(wrapper.text()).toContain('First.') + }) +}) diff --git a/mcp-docs/.vitepress/theme/composables/useCopy.ts b/mcp-docs/.vitepress/theme/composables/useCopy.ts new file mode 100644 index 00000000..d8832fe6 --- /dev/null +++ b/mcp-docs/.vitepress/theme/composables/useCopy.ts @@ -0,0 +1,28 @@ +import { onBeforeUnmount, ref } from 'vue' + +/** Clipboard copy with a short "copied" flag for button feedback. */ +export function useCopy(resetMs = 2000) { + const copied = ref(false) + let timer: ReturnType | undefined + + async function copy(text: string): Promise { + try { + await navigator.clipboard.writeText(text) + copied.value = true + if (timer) clearTimeout(timer) + timer = setTimeout(() => { + copied.value = false + }, resetMs) + return true + } catch { + copied.value = false + return false + } + } + + onBeforeUnmount(() => { + if (timer) clearTimeout(timer) + }) + + return { copied, copy } +} diff --git a/mcp-docs/.vitepress/theme/custom.css b/mcp-docs/.vitepress/theme/custom.css index 8c489782..be9b4028 100644 --- a/mcp-docs/.vitepress/theme/custom.css +++ b/mcp-docs/.vitepress/theme/custom.css @@ -86,6 +86,9 @@ .fyp-site-mcp .VPHomeHero .name { background: linear-gradient(90deg, var(--fyp-terminal-green), var(--fyp-cyan), var(--fyp-terminal-green)); background-size: 200% 100%; + /* the `background` shorthand above resets clip — restore text clipping */ + -webkit-background-clip: text; + background-clip: text; animation: fyp-gradient-shift 10s ease-in-out infinite; } @@ -206,3 +209,927 @@ max-width: 25%; } } + +.fyp-site-mcp .VPHomeFeatures .VPFeature .VPImage { + width: 28px; + height: 28px; + margin-bottom: 14px; +} + +.fyp-site-mcp .fyp-home-section { + max-width: 1152px; + margin: 1rem auto 3rem; + padding: 0 24px; +} + +@media (min-width: 960px) { + .fyp-site-mcp .fyp-home-section { + max-width: 720px; + } +} + +.fyp-site-mcp .fyp-home-section .fyp-door-strip { + padding: 0; + margin: 1rem 0; +} + +/* ============================================================ + * Doc rhythm — headings, tables, code, callouts + * ============================================================ */ +.fyp-site-mcp .vp-doc h2 { + margin-top: 2.5rem; + padding-top: 1.75rem; + border-top: 1px solid var(--vp-c-divider); +} + +/* EIP pages keep the h1 in Markdown (page title, outline, search) but show EipHeader instead */ +.fyp-site-mcp .vp-doc h1:has(+ .fyp-eip-header) { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.fyp-site-mcp .vp-doc h1 + h2, +.fyp-site-mcp .vp-doc .fyp-eip-header + h2, +.fyp-site-mcp .vp-doc h2:first-of-type { + border-top: none; + padding-top: 0; +} + +.fyp-site-mcp .vp-doc h3 { + margin-top: 1.75rem; +} + +.fyp-site-mcp .vp-doc table { + display: block; + max-width: 100%; + overflow-x: auto; + border-radius: 8px; + border: 1px solid var(--vp-c-divider); +} + +.fyp-site-mcp .vp-doc tr { + background: transparent; +} + +.fyp-site-mcp .vp-doc tr:nth-child(2n) { + background: var(--vp-c-bg-soft); +} + +.fyp-site-mcp .vp-doc th { + font-family: var(--fyp-font-mono); + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--vp-c-text-2); + background: var(--vp-c-bg-soft); +} + +.fyp-site-mcp .vp-doc th, +.fyp-site-mcp .vp-doc td { + border-color: var(--vp-c-divider); +} + +.fyp-site-mcp .vp-doc div[class*='language-'] { + border-radius: 8px; + border: 1px solid var(--vp-c-divider); +} + +.fyp-site-mcp .vp-doc .custom-block { + border-radius: 8px; + border-width: 0 0 0 3px; + border-style: solid; +} + +.fyp-site-mcp .vp-doc .custom-block.warning { + border-color: var(--fyp-amber); +} + +.fyp-site-mcp .vp-doc .custom-block.tip { + border-color: var(--fyp-terminal-green); +} + +.fyp-site-mcp .vp-doc .custom-block-title { + font-family: var(--fyp-font-mono); + font-size: 0.8rem; + letter-spacing: 0.04em; +} + +.fyp-site-mcp .vp-doc a { + text-underline-offset: 3px; +} + +/* ============================================================ + * Chips (fork, status, tool) + * ============================================================ */ +.fyp-chip { + display: inline-flex; + align-items: center; + padding: 0.12rem 0.55rem; + border-radius: 999px; + border: 1px solid var(--vp-c-divider); + font-family: var(--fyp-font-mono); + font-size: 0.7rem; + font-weight: 600; + line-height: 1.5; + text-decoration: none !important; + color: var(--vp-c-text-2); + background: var(--vp-c-bg); +} + +a.fyp-chip:hover { + border-color: var(--vp-c-brand-1); + color: var(--vp-c-brand-1); +} + +.fyp-chip--fork { + color: var(--vp-c-brand-2); + border-color: color-mix(in srgb, var(--vp-c-brand-1) 40%, transparent); + background: var(--vp-c-brand-soft); +} + +.fyp-chip--status { + color: var(--vp-c-brand-2); + border-color: var(--vp-c-brand-1); + text-transform: uppercase; + letter-spacing: 0.08em; +} + +/* ============================================================ + * CopyButton + * ============================================================ */ +.fyp-copy-btn { + flex: 0 0 auto; + min-height: 28px; + padding: 0.15rem 0.7rem; + border-radius: 6px; + border: 1px solid var(--vp-c-divider); + background: var(--vp-c-bg); + color: var(--vp-c-text-2); + font-family: var(--fyp-font-mono); + font-size: 0.72rem; + font-weight: 600; + cursor: pointer; + transition: + border-color 0.15s ease, + color 0.15s ease; +} + +.fyp-copy-btn:hover, +.fyp-copy-btn:focus-visible { + border-color: var(--vp-c-brand-1); + color: var(--vp-c-brand-1); +} + +.fyp-copy-btn--done { + border-color: var(--vp-c-brand-1); + color: var(--vp-c-brand-2); + background: var(--vp-c-brand-soft); +} + +/* ============================================================ + * PromptCard / PromptList — the star of the docs + * ============================================================ */ +.fyp-prompt-list { + margin: 1.25rem 0; +} + +.fyp-prompt-list__intro { + color: var(--vp-c-text-2); + margin: 0 0 1rem; +} + +.fyp-prompt-list__group + .fyp-prompt-list__group { + margin-top: 1.5rem; +} + +.fyp-prompt-list__label { + margin: 0 0 0.6rem !important; + font-family: var(--fyp-font-mono); + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--vp-c-text-2); +} + +.fyp-prompt-list__cards { + display: grid; + gap: 0.75rem; +} + +.fyp-prompt-card { + margin: 1rem 0; + padding: 0.75rem 1rem 0.85rem; + border: 1px solid var(--vp-c-divider); + border-left: 3px solid var(--fyp-terminal-green); + border-radius: 8px; + background: var(--vp-c-bg); + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; +} + +.fyp-prompt-list__cards .fyp-prompt-card { + margin: 0; +} + +.fyp-prompt-card:hover { + box-shadow: 0 2px 12px rgba(34, 197, 94, 0.12); +} + +.fyp-prompt-card__meta { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + min-height: 28px; +} + +.fyp-prompt-card__quote, +.fyp-site-mcp .vp-doc .fyp-prompt-card__quote { + margin: 0.35rem 0 0; + padding: 0; + border: none; + color: var(--vp-c-text-1); +} + +.fyp-prompt-card__quote p { + margin: 0; + font-size: 1rem; + line-height: 1.55; + overflow-wrap: anywhere; +} + +.fyp-prompt-card__quote p::before { + content: '“'; + color: var(--vp-c-brand-1); + font-family: var(--fyp-font-mono); +} + +.fyp-prompt-card__quote p::after { + content: '”'; + color: var(--vp-c-brand-1); + font-family: var(--fyp-font-mono); +} + +.fyp-prompt-card__hint, +.fyp-prompt-card__foot { + margin: 0.55rem 0 0 !important; + font-size: 0.8rem; + line-height: 1.5; + color: var(--vp-c-text-2); +} + +.fyp-prompt-card__hint-label { + margin-right: 0.5rem; + font-family: var(--fyp-font-mono); + font-size: 0.65rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--vp-c-brand-2); +} + +.fyp-prompt-card__foot { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 0.25rem 1rem; + padding-top: 0.5rem; + border-top: 1px dashed var(--vp-c-divider); +} + +.fyp-prompt-card__foot code { + font-size: 0.78rem; +} + +.fyp-prompt-card__more { + font-weight: 600; +} + +/* ============================================================ + * EipHeader / LeadQuestion + * ============================================================ */ +.fyp-eip-header { + margin: 0 0 2rem; + padding: 1rem 1.15rem; + border: 1px solid var(--vp-c-divider); + border-left: 3px solid var(--fyp-terminal-green); + border-radius: 8px; + background: var(--vp-c-bg-soft); +} + +.fyp-eip-header__row { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.5rem 1rem; +} + +.fyp-eip-header__id { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 0.25rem 0.75rem; + margin: 0 !important; + min-width: 0; +} + +.fyp-eip-header__number { + font-family: var(--fyp-font-mono); + font-size: 1.6rem; + font-weight: 800; + letter-spacing: -0.02em; + color: var(--vp-c-brand-1); +} + +.fyp-eip-header__title { + font-family: var(--fyp-font-mono); + font-size: 1.15rem; + font-weight: 600; + color: var(--vp-c-text-1); + overflow-wrap: anywhere; +} + +.fyp-eip-header__launch { + margin: 0.6rem 0 0 !important; + font-size: 0.85rem; + color: var(--vp-c-text-2); +} + +.fyp-eip-header__chips { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.4rem; + margin-top: 0.75rem; +} + +.fyp-eip-header__tools-label { + margin-left: 0.4rem; + font-family: var(--fyp-font-mono); + font-size: 0.65rem; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--vp-c-text-3); +} + +.fyp-eip-header__links { + margin: 0.75rem 0 0 !important; + font-size: 0.88rem; +} + +.fyp-lead-question, +.fyp-site-mcp .vp-doc .fyp-lead-question { + margin: 0.5rem 0 1.25rem; + font-size: 1.15rem; + line-height: 1.6; + font-weight: 500; + color: var(--vp-c-text-1); +} + +/* ============================================================ + * SpecTable + * ============================================================ */ +.fyp-spec-table { + margin: 1rem 0; + border: 1px solid var(--vp-c-divider); + border-radius: 8px; + overflow: hidden; +} + +.fyp-spec-table__row { + display: grid; + grid-template-columns: minmax(7rem, 11rem) 1fr; + gap: 0.25rem 1rem; + padding: 0.55rem 1rem; +} + +.fyp-spec-table__row:nth-child(even) { + background: var(--vp-c-bg-soft); +} + +.fyp-spec-table__row + .fyp-spec-table__row { + border-top: 1px solid var(--vp-c-divider); +} + +.fyp-spec-table dt { + font-family: var(--fyp-font-mono); + font-size: 0.72rem; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--vp-c-text-2); +} + +.fyp-spec-table dd { + margin: 0; + min-width: 0; + overflow-wrap: anywhere; +} + +/* ============================================================ + * Steps + * ============================================================ */ +.fyp-steps, +.fyp-site-mcp .vp-doc ol.fyp-steps { + list-style: none; + margin: 1rem 0; + padding: 0; +} + +.fyp-steps__item, +.fyp-site-mcp .vp-doc .fyp-steps__item { + position: relative; + display: flex; + gap: 0.9rem; + margin: 0; + padding: 0 0 1.1rem; +} + +.fyp-steps__item:not(:last-child)::before { + content: ''; + position: absolute; + left: 13px; + top: 28px; + bottom: 0; + border-left: 1px dashed var(--vp-c-divider); +} + +.fyp-steps__marker { + flex: 0 0 auto; + display: grid; + place-items: center; + width: 28px; + height: 28px; + border-radius: 50%; + border: 1px solid var(--vp-c-brand-1); + background: var(--vp-c-brand-soft); + color: var(--vp-c-brand-2); + font-family: var(--fyp-font-mono); + font-size: 0.78rem; + font-weight: 700; +} + +.fyp-steps__body { + min-width: 0; +} + +.fyp-steps__title { + margin: 0.1rem 0 0 !important; + font-weight: 600; +} + +.fyp-steps__detail { + margin: 0.2rem 0 0 !important; + font-size: 0.9rem; + color: var(--vp-c-text-2); +} + +/* ============================================================ + * EndpointCard / CodeSnippet + * ============================================================ */ +.fyp-endpoint-card { + margin: 1.25rem 0; + padding: 0.85rem 1rem; + border: 1px solid var(--vp-c-divider); + border-left: 3px solid var(--fyp-cyan); + border-radius: 8px; + background: var(--vp-c-bg); +} + +.fyp-endpoint-card__label { + margin: 0 0 0.4rem !important; + font-family: var(--fyp-font-mono); + font-size: 0.68rem; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--vp-c-text-2); +} + +.fyp-endpoint-card__row { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.5rem 1rem; +} + +.fyp-endpoint-card__url { + min-width: 0; + font-family: var(--fyp-font-mono); + font-size: 0.95rem; + font-weight: 600; + overflow-wrap: anywhere; + color: var(--vp-c-brand-2); +} + +.fyp-code-snippet { + margin: 0.75rem 0; + border: 1px solid var(--vp-c-divider); + border-radius: 8px; + overflow: hidden; + background: var(--vp-code-block-bg); +} + +.fyp-code-snippet__caption { + padding: 0.4rem 0.9rem 0; + font-size: 0.8rem; + color: var(--vp-c-text-2); +} + +.fyp-code-snippet__bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + padding: 0.35rem 0.6rem 0.35rem 0.9rem; + border-bottom: 1px solid var(--vp-c-divider); +} + +.fyp-code-snippet__lang { + font-family: var(--fyp-font-mono); + font-size: 0.68rem; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--vp-c-text-3); +} + +.fyp-code-snippet__pre { + margin: 0; + padding: 0.75rem 0.9rem; + overflow-x: auto; + font-family: var(--fyp-font-mono); + font-size: 0.82rem; + line-height: 1.6; +} + +.fyp-code-snippet__pre code { + background: none; + padding: 0; + color: var(--vp-code-block-color); + white-space: pre; +} + +/* ============================================================ + * ClientTabs + * ============================================================ */ +.fyp-client-tabs { + margin: 1rem 0 1.5rem; + border: 1px solid var(--vp-c-divider); + border-radius: 8px; + overflow: hidden; + background: var(--vp-c-bg); +} + +.fyp-client-tabs__list { + display: flex; + overflow-x: auto; + border-bottom: 1px solid var(--vp-c-divider); + background: var(--vp-c-bg-soft); +} + +.fyp-client-tabs__tab { + flex: 0 0 auto; + min-height: 44px; + padding: 0.5rem 1.1rem; + border: none; + border-bottom: 2px solid transparent; + background: none; + font-family: var(--fyp-font-mono); + font-size: 0.82rem; + font-weight: 600; + color: var(--vp-c-text-2); + cursor: pointer; +} + +.fyp-client-tabs__tab:hover { + color: var(--vp-c-text-1); +} + +.fyp-client-tabs__tab--active { + color: var(--vp-c-brand-1); + border-bottom-color: var(--vp-c-brand-1); + background: var(--vp-c-bg); +} + +.fyp-client-tabs__panel { + padding: 1rem 1.15rem 0.5rem; +} + +.fyp-client-tabs__panel > p:first-child { + margin-top: 0; +} + +.fyp-client-tabs__note { + font-size: 0.85rem; + color: var(--vp-c-text-2); +} + +.fyp-client-tabs__steps { + margin: 0.5rem 0; + padding-left: 1.25rem; +} + +.fyp-client-tabs__table { + display: table !important; + width: 100%; +} + +.fyp-install-link, +.fyp-site-mcp .vp-doc a.fyp-install-link { + display: inline-flex; + align-items: center; + min-height: 44px; + padding: 0.5rem 1.2rem; + border-radius: 8px; + background: var(--vp-c-brand-3); + color: #fff; + font-family: var(--fyp-font-mono); + font-weight: 700; + text-decoration: none; + transition: background 0.15s ease; +} + +.fyp-install-link:hover, +.fyp-site-mcp .vp-doc a.fyp-install-link:hover { + background: var(--vp-c-brand-2); + color: #fff; +} + +/* ============================================================ + * LaunchFacts / IconGrid / IconNote / DoorStrip + * ============================================================ */ +.fyp-launch-facts { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.75rem; + margin: 1.25rem 0; +} + +.fyp-launch-facts__item { + padding: 0.7rem 0.9rem; + border: 1px solid var(--vp-c-divider); + border-radius: 8px; + background: var(--vp-c-bg-soft); +} + +.fyp-launch-facts__title { + margin: 0 !important; + font-family: var(--fyp-font-mono); + font-size: 0.68rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--vp-c-text-2); +} + +.fyp-launch-facts__detail { + margin: 0.25rem 0 0 !important; + font-size: 0.92rem; +} + +.fyp-icon-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.75rem; + margin: 1.25rem 0; +} + +.fyp-icon-grid--three { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.fyp-icon-card { + display: block; + padding: 0.9rem 1rem; + border: 1px solid var(--vp-c-divider); + border-left: 3px solid var(--fyp-terminal-green); + border-radius: 8px; + background: var(--vp-c-bg); + color: inherit; + text-decoration: none !important; + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; +} + +.fyp-icon-grid > .fyp-icon-card:nth-child(4n + 2) { + border-left-color: var(--fyp-cyan); +} + +.fyp-icon-grid > .fyp-icon-card:nth-child(4n + 3) { + border-left-color: #0ea5e9; +} + +.fyp-icon-grid > .fyp-icon-card:nth-child(4n + 4) { + border-left-color: var(--fyp-amber); +} + +.fyp-icon-card--link:hover { + box-shadow: 0 2px 12px rgba(34, 197, 94, 0.14); +} + +.fyp-icon-card__glyph { + width: 24px; + height: 24px; + margin-bottom: 0.4rem; + color: var(--vp-c-brand-1); +} + +.fyp-icon-card__title { + margin: 0 !important; + font-family: var(--fyp-font-mono); + font-size: 0.9rem; + font-weight: 700; + color: var(--vp-c-text-1); +} + +.fyp-icon-card__detail { + margin: 0.25rem 0 0 !important; + font-size: 0.88rem; + line-height: 1.55; + color: var(--vp-c-text-2); +} + +.fyp-icon-note { + display: flex; + gap: 0.9rem; + margin: 1.25rem 0 0.5rem; + padding: 0.8rem 1rem; + border: 1px solid var(--vp-c-divider); + border-left: 3px solid var(--fyp-terminal-green); + border-radius: 8px; + background: var(--vp-c-bg-soft); +} + +.fyp-icon-note__mark { + flex: 0 0 auto; + display: grid; + place-items: center; + width: 36px; + height: 36px; + border-radius: 8px; + background: var(--vp-c-brand-soft); +} + +.fyp-icon-note__glyph { + width: 22px; + height: 22px; + color: var(--vp-c-brand-1); +} + +.fyp-icon-note__copy { + min-width: 0; +} + +.fyp-icon-note__title { + margin: 0 !important; + font-family: var(--fyp-font-mono); + font-weight: 700; +} + +.fyp-icon-note__body { + font-size: 0.92rem; + color: var(--vp-c-text-2); +} + +.fyp-icon-note__body > :first-child { + margin-top: 0.2rem; +} + +.fyp-icon-note__body > :last-child { + margin-bottom: 0; +} + +.fyp-door-strip { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 0.75rem; + max-width: 1152px; + margin: 2rem auto; + padding: 0 24px; +} + +.fyp-door-strip__item { + display: block; + padding: 0.8rem 1rem; + border: 1px solid var(--vp-c-divider); + border-radius: 8px; + background: var(--vp-c-bg); + text-decoration: none !important; + color: inherit; + transition: border-color 0.2s ease; +} + +.fyp-door-strip__item:hover { + border-color: var(--vp-c-brand-1); +} + +.fyp-door-strip__title, +.fyp-door-strip__detail { + display: block; +} + +.fyp-door-strip__title { + margin: 0 !important; + font-family: var(--fyp-font-mono); + font-weight: 700; + color: var(--vp-c-brand-1); +} + +.fyp-door-strip__detail { + margin: 0.2rem 0 0 !important; + font-size: 0.85rem; + color: var(--vp-c-text-2); +} + +/* ============================================================ + * CollapsibleChangelog + * ============================================================ */ +.fyp-changelog-collapse { + margin: 2rem 0 1rem; + border: 1px solid var(--vp-c-divider); + border-radius: 8px; + background: var(--vp-c-bg-soft); +} + +.fyp-changelog-collapse > .fyp-changelog { + margin: 0; + border: none; + border-top: 1px solid var(--vp-c-divider); + border-radius: 0; +} + +.fyp-changelog-collapse__summary { + padding: 0.6rem 1rem; + cursor: pointer; + font-family: var(--fyp-font-mono); + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--vp-c-text-2); +} + +.fyp-changelog-collapse__summary:hover { + color: var(--vp-c-brand-1); +} + +/* ============================================================ + * Dark-mode contrast + mobile + * ============================================================ */ +.dark.fyp-site-mcp .fyp-prompt-card, +.fyp-site-mcp.dark .fyp-prompt-card, +.fyp-site-mcp.dark .fyp-icon-card, +.fyp-site-mcp.dark .fyp-endpoint-card { + background: var(--vp-c-bg-soft); +} + +.fyp-site-mcp.dark .fyp-chip { + background: var(--vp-c-bg-soft); +} + +.fyp-site-mcp.dark .fyp-chip--fork { + background: var(--vp-c-brand-soft); +} + +.fyp-site-mcp.dark .fyp-install-link, +.fyp-site-mcp.dark .vp-doc a.fyp-install-link { + background: var(--vp-c-brand-3); +} + +@media (max-width: 720px) { + .fyp-icon-grid, + .fyp-icon-grid--three { + grid-template-columns: minmax(0, 1fr); + } + + .fyp-spec-table__row { + grid-template-columns: minmax(0, 1fr); + } + + .fyp-eip-header__number { + font-size: 1.35rem; + } + + .fyp-prompt-card { + padding: 0.7rem 0.8rem 0.8rem; + } + + .fyp-client-tabs__panel { + padding: 0.85rem 0.85rem 0.35rem; + } + + .fyp-door-strip { + padding: 0 16px; + } +} diff --git a/mcp-docs/.vitepress/theme/icons.ts b/mcp-docs/.vitepress/theme/icons.ts new file mode 100644 index 00000000..36f4f765 --- /dev/null +++ b/mcp-docs/.vitepress/theme/icons.ts @@ -0,0 +1,57 @@ +import type { Component } from 'vue' +import { + ArrowRightIcon, + ArrowsRightLeftIcon, + BanknotesIcon, + BeakerIcon, + BoltIcon, + BookOpenIcon, + CalendarDaysIcon, + CheckCircleIcon, + ClockIcon, + CommandLineIcon, + CpuChipIcon, + CubeIcon, + DocumentTextIcon, + InboxArrowDownIcon, + MapIcon, + NoSymbolIcon, + ScaleIcon, + ServerStackIcon, + ShieldCheckIcon, + SparklesIcon, + Square3Stack3DIcon, + UserGroupIcon, +} from '@heroicons/vue/24/outline' + +/** Outline Heroicons for MCP docs components (same ids as the roadmap set). */ +export const MCP_ICONS = { + spark: SparklesIcon, + cube: CubeIcon, + book: BookOpenIcon, + terminal: CommandLineIcon, + people: UserGroupIcon, + chip: CpuChipIcon, + beaker: BeakerIcon, + stack: Square3Stack3DIcon, + shield: ShieldCheckIcon, + scale: ScaleIcon, + inbox: InboxArrowDownIcon, + trace: DocumentTextIcon, + boundary: NoSymbolIcon, + calendar: CalendarDaysIcon, + split: ArrowsRightLeftIcon, + map: MapIcon, + token: BanknotesIcon, + check: CheckCircleIcon, + bolt: BoltIcon, + arrow: ArrowRightIcon, + clock: ClockIcon, + server: ServerStackIcon, +} as const satisfies Record + +export type McpIconId = keyof typeof MCP_ICONS + +export function mcpIcon(icon: string): Component | undefined { + return MCP_ICONS[icon as McpIconId] +} diff --git a/mcp-docs/.vitepress/theme/index.ts b/mcp-docs/.vitepress/theme/index.ts index ce66c9e8..5e9a45ec 100644 --- a/mcp-docs/.vitepress/theme/index.ts +++ b/mcp-docs/.vitepress/theme/index.ts @@ -3,11 +3,26 @@ import DefaultTheme from 'vitepress/theme' import { onMounted } from 'vue' import Changelog from './components/Changelog.vue' +import ClientTabs from './components/ClientTabs.vue' +import CodeSnippet from './components/CodeSnippet.vue' +import CollapsibleChangelog from './components/CollapsibleChangelog.vue' +import CopyButton from './components/CopyButton.vue' +import DoorStrip from './components/DoorStrip.vue' +import EipHeader from './components/EipHeader.vue' +import EndpointCard from './components/EndpointCard.vue' +import IconGrid from './components/IconGrid.vue' +import IconNote from './components/IconNote.vue' +import LaunchFacts from './components/LaunchFacts.vue' +import LeadQuestion from './components/LeadQuestion.vue' +import PromptCard from './components/PromptCard.vue' +import PromptList from './components/PromptList.vue' +import SpecTable from './components/SpecTable.vue' +import Steps from './components/Steps.vue' import './custom.css' /** - * MCP docs theme — "Machine Room" skin (terminal-green, see custom.css) plus - * globally registered Changelog for per-section micro-versioning. + * MCP docs theme — "Machine Room" skin (see custom.css) plus globally + * registered components for prompts, setup, and EIP catalogue pages. */ export default { extends: DefaultTheme, @@ -18,5 +33,20 @@ export default { }, enhanceApp({ app }) { app.component('Changelog', Changelog) + app.component('CollapsibleChangelog', CollapsibleChangelog) + app.component('ClientTabs', ClientTabs) + app.component('CodeSnippet', CodeSnippet) + app.component('CopyButton', CopyButton) + app.component('DoorStrip', DoorStrip) + app.component('EipHeader', EipHeader) + app.component('EndpointCard', EndpointCard) + app.component('IconGrid', IconGrid) + app.component('IconNote', IconNote) + app.component('LaunchFacts', LaunchFacts) + app.component('LeadQuestion', LeadQuestion) + app.component('PromptCard', PromptCard) + app.component('PromptList', PromptList) + app.component('SpecTable', SpecTable) + app.component('Steps', Steps) }, } satisfies Theme diff --git a/mcp-docs/.vitepress/theme/mcpConstants.ts b/mcp-docs/.vitepress/theme/mcpConstants.ts new file mode 100644 index 00000000..31daf3b2 --- /dev/null +++ b/mcp-docs/.vitepress/theme/mcpConstants.ts @@ -0,0 +1,44 @@ +/** Hosted MCP endpoint — keep in sync with use/connect.md */ +export const FYP_MCP_URL = 'https://mcp.feelyourprotocol.org/mcp' + +export const FYP_MCP_SERVER_NAME = 'feel-your-protocol' + +/** MCP tool id to docs path (stable URLs). */ +export const TOOL_DOC_PATH: Record = { + describe_capabilities: '/use/tools/describe-capabilities', + run_bytecode: '/use/tools/run-bytecode', + run_transaction: '/use/tools/run-transaction', + run_block: '/use/tools/run-block', + generate_artifact: '/use/tools/generate-artifact', + inspect_artifact: '/use/tools/inspect-artifact', +} + +/** + * Cursor install deeplink: base64 of the single-server config object + * (https://cursor.com/docs/mcp/install-links). + */ +export function cursorMcpInstallLink(name: string, config: Record): string { + const json = JSON.stringify(config) + const encoded = + typeof btoa === 'function' ? btoa(json) : Buffer.from(json, 'utf8').toString('base64') + return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(name)}&config=${encoded}` +} + +export const CURSOR_INSTALL_LINK = cursorMcpInstallLink(FYP_MCP_SERVER_NAME, { url: FYP_MCP_URL }) + +export const CURSOR_MCP_JSON = `{ + "mcpServers": { + "${FYP_MCP_SERVER_NAME}": { + "url": "${FYP_MCP_URL}" + } + } +}` + +/** Claude Code CLI — `claude mcp add --transport http `. */ +export const CLAUDE_CODE_ADD_COMMAND = `claude mcp add --transport http ${FYP_MCP_SERVER_NAME} ${FYP_MCP_URL}` + +/** Codex CLI — `codex mcp add --url ` (streamable HTTP). */ +export const CODEX_ADD_COMMAND = `codex mcp add ${FYP_MCP_SERVER_NAME} --url ${FYP_MCP_URL}` + +export const CODEX_CONFIG_TOML = `[mcp_servers.${FYP_MCP_SERVER_NAME}] +url = "${FYP_MCP_URL}"` diff --git a/mcp-docs/__tests__/eipPages.spec.ts b/mcp-docs/__tests__/eipPages.spec.ts new file mode 100644 index 00000000..af816981 --- /dev/null +++ b/mcp-docs/__tests__/eipPages.spec.ts @@ -0,0 +1,30 @@ +import { readdirSync, readFileSync } from 'node:fs' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' + +const DIR = join(process.cwd(), 'mcp-docs/use/eips') +const files = readdirSync(DIR).filter((f) => /^eip-\d+\.md$/.test(f)) + +describe('EIP page template', () => { + it('covers all twelve catalogue pages', () => { + expect(files).toHaveLength(12) + }) + + it.each(files)('%s uses the shared presentation components', (file) => { + const text = readFileSync(join(DIR, file), 'utf8') + const number = file.match(/eip-(\d+)\.md/)?.[1] + expect(text.startsWith(`# EIP-${number} — `)).toBe(true) + expect(text).toContain(' { + const text = readFileSync(join(DIR, file), 'utf8') + expect(text.match(/text: `[^`]{15,}`/g)?.length ?? 0).toBeGreaterThanOrEqual(3) + }) +}) diff --git a/mcp-docs/__tests__/urlStability.spec.ts b/mcp-docs/__tests__/urlStability.spec.ts new file mode 100644 index 00000000..11ccdaf1 --- /dev/null +++ b/mcp-docs/__tests__/urlStability.spec.ts @@ -0,0 +1,65 @@ +import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' + +const ROOT = join(process.cwd(), 'mcp-docs') + +function markdownFiles(dir: string): string[] { + return readdirSync(dir).flatMap((name) => { + const path = join(dir, name) + if (name === 'node_modules' || name.startsWith('.') || name === 'public') return [] + if (statSync(path).isDirectory()) return markdownFiles(path) + return name.endsWith('.md') && name !== 'README.md' ? [path] : [] + }) +} + +function docUrl(path: string): string { + return ( + '/' + + path + .slice(ROOT.length + 1) + .replace(/\.md$/, '') + .replace(/^index$/, '') + ) +} + +describe('mcp-docs URL stability', () => { + const pages = markdownFiles(ROOT).map(docUrl) + + it('does not drop pages that are already public', () => { + expect(pages.filter((p) => p.startsWith('/use/eips/'))).toHaveLength(12) + expect(pages.filter((p) => p.startsWith('/use/')).length).toBeGreaterThanOrEqual(20) + expect(pages.filter((p) => p.startsWith('/internals/')).length).toBeGreaterThanOrEqual(8) + for (const required of [ + '/use/connect', + '/use/introduction', + '/use/capabilities', + '/use/coverage', + '/use/forks/glamsterdam', + '/use/forks/fusaka', + '/use/tools/run-transaction', + ]) { + expect(pages).toContain(required) + } + }) + + it('every internal nav and sidebar link in config.ts resolves to a page', () => { + const config = readFileSync(join(ROOT, '.vitepress/config.ts'), 'utf8') + const links = [...config.matchAll(/link: '(\/[^']*)'/g)].map((m) => m[1]) + expect(links.length).toBeGreaterThan(15) + for (const link of links) { + expect(existsSync(join(ROOT, `${link}.md`)), `missing page for ${link}`).toBe(true) + } + }) + + it('every internal Markdown link points at an existing page', () => { + const broken: string[] = [] + for (const file of markdownFiles(ROOT)) { + const text = readFileSync(file, 'utf8') + for (const match of text.matchAll(/(?:\]\(|href[:=]\s*["'])(\/(?:use|internals)\/[^)"'#\s]+)/g)) { + if (!existsSync(join(ROOT, `${match[1]}.md`))) broken.push(`${file}: ${match[1]}`) + } + } + expect(broken).toEqual([]) + }) +}) diff --git a/mcp-docs/index.md b/mcp-docs/index.md index f8bcd634..cde0d229 100644 --- a/mcp-docs/index.md +++ b/mcp-docs/index.md @@ -14,16 +14,61 @@ hero: link: /use/introduction features: - - title: Connect + - icon: + src: /icons/connect.svg + width: 28 + height: 28 + title: Connect details: Cursor, Claude, Codex — one hosted URL and a first prompt to try when we go live. link: /use/connect - - title: Amsterdam now + - icon: + src: /icons/amsterdam.svg + width: 28 + height: 28 + title: Amsterdam now details: Full Glamsterdam hardfork at launch. Gas, receipts, and bytecode under upcoming rules. link: /use/forks/glamsterdam - - title: What you can ask + - icon: + src: /icons/ask.svg + width: 28 + height: 28 + title: What you can ask details: Wallet gas, opcodes, storage pricing, lab blocks — five jobs, no tool memorization. link: /use/capabilities - - title: Browser explorations + - icon: + src: /icons/explore.svg + width: 28 + height: 28 + title: Browser explorations details: Prefer clicking through a change first? Same questions on feelyourprotocol.org. link: https://feelyourprotocol.org --- + +
+ +## Your first question + +Once you are connected, paste this into your agent. No EIP number, no JSON. + + + +## Find your way + + + +
diff --git a/mcp-docs/public/icons/amsterdam.svg b/mcp-docs/public/icons/amsterdam.svg new file mode 100644 index 00000000..b384ed68 --- /dev/null +++ b/mcp-docs/public/icons/amsterdam.svg @@ -0,0 +1 @@ + diff --git a/mcp-docs/public/icons/ask.svg b/mcp-docs/public/icons/ask.svg new file mode 100644 index 00000000..6aacdf4b --- /dev/null +++ b/mcp-docs/public/icons/ask.svg @@ -0,0 +1 @@ + diff --git a/mcp-docs/public/icons/connect.svg b/mcp-docs/public/icons/connect.svg new file mode 100644 index 00000000..1b775330 --- /dev/null +++ b/mcp-docs/public/icons/connect.svg @@ -0,0 +1 @@ + diff --git a/mcp-docs/public/icons/explore.svg b/mcp-docs/public/icons/explore.svg new file mode 100644 index 00000000..add7a37d --- /dev/null +++ b/mcp-docs/public/icons/explore.svg @@ -0,0 +1 @@ + diff --git a/mcp-docs/use/capabilities.md b/mcp-docs/use/capabilities.md index 4b6c5f48..867f47a4 100644 --- a/mcp-docs/use/capabilities.md +++ b/mcp-docs/use/capabilities.md @@ -4,62 +4,117 @@ You do not talk to the EVM in JSON. You talk to **your agent**, and the agent ca ## 1. Wallet and transaction gas -**When it fits:** intrinsic gas, self-send vs transfer, contract creation limits, receipt logs on value moves, “what gasLimit should my wallet show?” - -**Example prompts** - -- *“What intrinsic gas does a plain ETH transfer use on Amsterdam?”* → [EIP-2780](/use/eips/eip-2780) -- *“This calldata is mostly zeros. What does it cost on Amsterdam versus Fusaka?”* → [EIP-7976](/use/eips/eip-7976) -- *“Deploy a contract this size on Amsterdam — do I hit the limit?”* → [EIP-7954](/use/eips/eip-7954) -- *“Does this transfer emit a log in the receipt on Amsterdam?”* → [EIP-7708](/use/eips/eip-7708) -- *“First 1 wei to an empty account — break down regular vs state gas.”* → [EIP-8037](/use/eips/eip-8037) + + +Intrinsic gas, self-send vs transfer, contract creation limits, receipt logs on value moves, “what gasLimit should my wallet show?” + + + + **Compare (only when you ask):** run the same transfer on **Fusaka** then **Amsterdam** and diff `gasUsed`. ## 2. Bytecode, opcodes, and precompiles -**When it fits:** stack opcodes, ModExp cost curves, P-256 verify, arbitrary bytecode under a fork. + -**Example prompts** +Stack opcodes, ModExp cost curves, P-256 verify, arbitrary bytecode under a fork. -- *“Run this bytecode on Amsterdam and show gas and the final stack.”* -- *“Call ModExp with these inputs — gas on Pectra vs Fusaka.”* → [EIP-7883](/use/eips/eip-7883) -- *“Does secp256r1 precompile accept this signature on mainnet rules?”* → [EIP-7951](/use/eips/eip-7951) -- *“Exercise DUPN/SWAPN on Amsterdam.”* → [EIP-8024](/use/eips/eip-8024) + + + You supply bytecode (and optional [demo accounts](/use/tools/run-bytecode#byos-prestate-accounts)) — the server does not ship demo contracts. ## 3. Storage gas and program gas -**When it fits:** SSTORE/SLOAD pricing, existing-slot vs new-slot behavior, Glamsterdam **state gas** split. + -**Example prompts** +SSTORE/SLOAD pricing, existing-slot vs new-slot behavior, Glamsterdam **state gas** split. -- *“SSTORE slot 3 from cold — Amsterdam vs Fusaka.”* → [EIP-8038](/use/eips/eip-8038) -- *“Seed storage in accounts[] and run this bytecode — what gas?”* + + + Bytecode path vs transaction path: program gas often belongs in a bytecode run; paid tx totals belong in a transaction run. Your agent can choose; both are supported for 8038. ## 4. Small lab blocks -**When it fits:** several transactions in one block, header slot/number, per-tx receipts together. + + +Several transactions in one block, header slot/number, per-tx receipts together. -**Example prompts** + -- *“Run these two transfers as one Amsterdam block and show each receipt.”* -- *“Set header slot and run one tx — what changes?”* → [EIP-7843](/use/eips/eip-7843) context -- *“Clear a storage slot on Amsterdam. What do I pay, and what does the block count?”* → [EIP-7778](/use/eips/eip-7778) + Up to **8** transactions per block. Not historical chain replay. ## 5. Artifacts without executing chain history -**When it fits:** block access list JSON, structure checks, hashes — caller-supplied blobs. + -**Example prompts** +Block access list JSON, structure checks, hashes — caller-supplied blobs. -- *“Generate a BAL for this lab block under Amsterdam.”* → [EIP-7928](/use/eips/eip-7928) -- *“Inspect this BAL JSON — valid structure and hash?”* + + + ## What we do not do @@ -81,11 +136,10 @@ The agent maps your question to a small set of MCP tools. You rarely need these | Multi-tx lab block | `run_block` | | BAL generate / inspect | `generate_artifact`, `inspect_artifact` | -## Changelog - - -```json -{ - "mcpServers": { - "feel-your-protocol": { - "url": "https://mcp.feelyourprotocol.org/mcp" - } - } -} -``` +For browser learning, use **[feelyourprotocol.org](https://feelyourprotocol.org)**. -3. Restart Claude Desktop. -4. Start a new conversation and ask the [first prompt](#after-you-connect-try-this-first). +## Add the server to your agent -If your Claude product only lists pre-approved connectors today, save this config and retry at launch — we document the stable URL here. + ---- +Pick your client. Each tab has the shortest path for that client. -## OpenAI Codex / CLI agents + -Point your MCP-capable Codex or agent CLI at the same HTTP endpoint. Exact flag names depend on the client; the invariant is: +## After you connect — try this first {#after-you-connect-try-this-first} -- **Transport:** HTTP MCP at `https://mcp.feelyourprotocol.org/mcp` -- **Discovery:** allow the host to list tools from the server -- **First test:** the [8037 prompt](#after-you-connect-try-this-first) +Ask your agent: -When OpenAI ships or updates Codex MCP wiring, this URL stays the single integration point — no per-EIP endpoints. + ---- +That exercises first-touch state gas ([EIP-8037](/use/eips/eip-8037)) without you naming tools or JSON fields. Then browse [Amsterdam now](/use/forks/glamsterdam) for more prompts. -## Other MCP hosts +After connect, prefer natural-language questions. The agent should call the server’s generic tools (`run_bytecode`, `run_transaction`, `run_block`, …) — you do not need to memorize them. Optional compare: ask for the **same question on Fusaka then Amsterdam** and diff gas or receipts. -Any host that supports **remote MCP over HTTP** can use: +## If it does not work -| Field | Value | +| Symptom | Try this | | --- | --- | -| URL | `https://mcp.feelyourprotocol.org/mcp` | -| Auth at launch | none | - -After connect, prefer natural-language questions. The agent should call the server’s generic tools (`run_bytecode`, `run_transaction`, `run_block`, …) — you do not need to memorize them. Optional compare: ask for the **same question on Fusaka then Amsterdam** and diff gas or receipts. +| No tools show up | Restart the client or reload MCP servers, then check that the URL ends in `/mcp`. | +| Connection error before launch | The hosted endpoint is not live until launch week. Keep the config and retry once we announce it. | +| Connection error after launch | Update your client — remote HTTP MCP needs a recent build. | +| Claude does not list the server | Add it as a custom connector under Customize → Connectors, not in the local config file. | +| The agent answers without calling a tool | Ask it to run the simulation on the MCP server, and name the fork (Amsterdam or Fusaka). | ## What the server exposes At launch you get probe + run + artifact tools covering Amsterdam and the earlier fork lineage. Machine-readable schemas live under [Reference → Tool schemas](/use/tools/describe-capabilities). Human “what can I ask?” lives on [What you can ask](/use/capabilities). -## Changelog - - ## What became possible -**Why does sending ETH to someone else still cost 21,000 gas, while sending it to yourself costs 12,000?** EIP-2780 replaces the flat intrinsic charge with named pieces. A value transfer to an existing account is still 12,000 for the sender, plus 3,000 to touch the recipient, plus 6,000 to move the value. A self-send keeps only the 12,000. A call that moves no value is 15,000. Fusaka charges 21,000 for all three. - -The MCP server runs **your** transaction. It does not ship demo transactions. - -## What you can ask your agent + -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +EIP-2780 replaces the flat intrinsic charge with named pieces. A value transfer to an existing account is still 12,000 for the sender, plus 3,000 to touch the recipient, plus 6,000 to move the value. A self-send keeps only the 12,000. A call that moves no value is 15,000. Fusaka charges 21,000 for all three. -- _“On Glamsterdam, what does a 1 wei transfer to an existing account cost, compared with Fusaka?”_ -- _“What does a self-transfer cost, and a zero-value call to an existing account?”_ -- _“Does a contract creation with value cost more execution gas than one without?”_ -- _“If gas covers 21,000 but not a new-account charge, is the transaction rejected or included and reverted?”_ +The MCP server runs **your** transaction. It does not ship demo transactions. -These are **inspiration prompts** — curiosity, understanding, or builder checks. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. They are starting points — curiosity, understanding, or builder checks. + + ## What the server does -| Step | Action | Purpose | -| ---- | ------ | ------- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm the 2780 row; `tools` is `run_transaction` | -| 2 | [Run transaction](/use/tools/run-transaction) | Same transfer on **`fusaka`** then **`glamsterdam`**; read `gasUsed` | + Prefund the recipient when it should already exist, so the run does not also create an account. A self-send uses the same address for sender and recipient. -## Fork caveat - +::: warning Fork caveat Run the **same transaction twice** — **`fusaka`** (baseline), then **`glamsterdam`** (preview). A value transfer to an existing account stays **21,000** on both, with no state gas. A self-send drops to **12,000** only on Glamsterdam. A zero-value call drops to **15,000** only on Glamsterdam. The piece names are not separate result fields; the total is `gasUsed`. Creating a new account still adds state gas. That charge is [EIP-8037](/use/eips/eip-8037), not this split. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-2780 on feelyourprotocol.org](https://feelyourprotocol.org/eip-2780-intrinsic-transaction-gas) | -| **Canonical spec** | [EIP-2780](https://github.com/ethereum/EIPs/blob/8331fb3eed0a5366b28b25a016f1ad04fac0fa8e/EIPS/eip-2780.md) | -| **Spec date** | 2026-08-04 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**When does native ETH show up as a Transfer log — and when does it stay invisible to log filters?** EIP-7708 emits synthetic ERC-20-compatible `Transfer` logs from the system address for nonzero ETH moves (tx value, `CALL`, `CREATE`/`CREATE2`, `SELFDESTRUCT`). - -The MCP server runs a **value-bearing transaction** (`run_transaction`) and returns **`logs`** plus **`decodedLogs`** (EIP-7708 Transfer/Burn decorations when present). Compare **Fusaka** vs **Glamsterdam** on the same tx — preview emits Transfer logs; baseline stays silent. - -## What you can ask your agent + -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +EIP-7708 emits synthetic ERC-20-compatible `Transfer` logs from the system address for nonzero ETH moves (tx value, `CALL`, `CREATE`/`CREATE2`, `SELFDESTRUCT`). -- *“Run a plain 1 wei transfer on Glamsterdam — decode the EIP-7708 Transfer log in the result.”* -- *“Same transfer on Fusaka vs Glamsterdam — which decoded Transfer logs differ?”* -- *“My contract wallet forwards ETH via CALL — show me the Transfer log fields (from, to, value).”* -- *“Why is there no Transfer log when the inner CALL reverts?”* +The MCP server runs a **value-bearing transaction** (`run_transaction`) and returns **`logs`** plus **`decodedLogs`** (EIP-7708 Transfer/Burn decorations when present). Compare **Fusaka** vs **Glamsterdam** on the same tx — preview emits Transfer logs; baseline stays silent. -These are **inspiration prompts** — curiosity, understanding, or builder checks. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. They are starting points — curiosity, understanding, or builder checks. + + ## What the server does -| Step | Action | Purpose | -| --- | --- | --- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 7708 catalog entry; `tools` include `run_transaction` | -| 2 | [Run transaction](/use/tools/run-transaction) | Execute on **`glamsterdam`** or **`fusaka`**; read `logs` / `decodedLogs` | + ```json { @@ -37,23 +96,23 @@ These are **inspiration prompts** — curiosity, understanding, or builder check Optional **`code`** installs runtime bytecode at `to` before the tx (contract-wallet style scenarios). Inner CALL programs may also use [Run bytecode](/use/tools/run-bytecode). -## Fork caveat - +::: warning Fork caveat Run the **same transaction twice** — **`fusaka`** (baseline), then **`glamsterdam`** (preview) — and compare **`decodedLogs`**. EIP-7708 Transfer decorations appear only on Glamsterdam for successful nonzero value moves. Do not infer Transfer events from opcode traces alone. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-7708 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7708-eth-transfer-logs) | -| **Canonical spec** | [EIP-7708](https://github.com/ethereum/EIPs/blob/f7230c46a743313957d8f38a159bda934cc735b2/EIPS/eip-7708.md) | -| **Spec date** | 2026-07-10 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**I cleared storage and got a refund — why does the block still count the full gas?** EIP-7778 keeps the refund on the sender's bill. The block still counts the gas from before that refund. A storage clear that creates no new state shows two numbers on Glamsterdam (you pay less than the block counts) and one number on Fusaka. + -Warm access and putting a slot back to its original value still reduce what the block counts. Those are cheaper work, not a refund subtracted after the fact. +EIP-7778 keeps the refund on the sender's bill. The block still counts the gas from before that refund. A storage clear that creates no new state shows two numbers on Glamsterdam (you pay less than the block counts) and one number on Fusaka. -## What you can ask your agent +Warm access and putting a slot back to its original value still reduce what the block counts. Those are cheaper work, not a refund subtracted after the fact. -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +## Try asking your agent -- _“Clear a nonzero storage slot on Glamsterdam. What do I pay, and what does the block count?”_ -- _“Same clear on Fusaka — do the two numbers match?”_ -- _“Rewrite a nonzero slot to another nonzero value. Does the bill still match the block?”_ -- _“If I write a new value and then put the original back, does the block still get the discount?”_ -- _“Can a block of storage-clearing transactions sneak past the gas limit because of refunds?”_ +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. They are starting points — curiosity, understanding, or builder checks. -These are **inspiration prompts** — curiosity, understanding, or builder checks. + ## What the server does -| Step | Action | Purpose | -| ---- | ------ | ------- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 7778 catalog entry; `tools` include `run_transaction` and `run_block` | -| 2 | [Run transaction](/use/tools/run-transaction) | Paid `gasUsed` vs `txRegularGas` on Glamsterdam | -| 3 | [Run block](/use/tools/run-block) | `header.gasUsed` vs `transactions[].gasUsed` | + ```json { @@ -47,24 +116,24 @@ These are **inspiration prompts** — curiosity, understanding, or builder check A lab block with the same transaction reports the gap as `header.gasUsed` versus `transactions[].gasUsed`. -## Fork caveat - +::: warning Fork caveat Run the **same transaction twice** — **`fusaka`** (baseline), then **`glamsterdam`** (preview). Lead with paid `gasUsed` against the block count (`txRegularGas` or `header.gasUsed`). Receipt gas follows what the sender pays in this lab. The block count is the separate figure. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-7778 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7778-block-gas-accounting) | -| **Canonical spec** | [EIP-7778](https://github.com/ethereum/EIPs/blob/3929b1aab57b493417eccec7457d1485eccb9768/EIPS/eip-7778.md) | -| **Spec date** | 2026-01-28 | -| **Status** | Draft | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**How do you read the beacon slot on-chain without hardcoding 12-second slots?** EIP-7843 adds **`SLOTNUM`** (`0x4b`): the consensus layer writes the slot on the execution header, and the opcode pushes it for 2 gas. + + +EIP-7843 adds **`SLOTNUM`** (`0x4b`): the consensus layer writes the slot on the execution header, and the opcode pushes it for 2 gas. Dividing the block timestamp by 12 works only while slots stay 12 seconds. The opcode abstracts that away. The MCP server runs **your** bytecode in a lab block with a chosen `header.slotNumber` and returns the per-tx result (return data, gas) plus a header snapshot. It does not ship demo programs. -## What you can ask your agent - -Once the public MCP is live ([Connect](/use/connect)), try prompts like: - -- *“What does SLOTNUM push if the header slot is 42?”* -- *“Same SLOTNUM program on Fusaka vs Glamsterdam — does 0x4b succeed?”* -- *“Why not TIMESTAMP ÷ 12 for the current slot?”* -- *“Here’s bytecode that returns SLOTNUM — what does it return on slot 12345678?”* - -These are **inspiration prompts** — ask in plain language; your agent uses the connected server and reports results back to you. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. Ask in plain language; your agent uses the connected server and reports results back to you. + + ## What the server does -| Step | Action | Purpose | -| --- | --- | --- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 7843 is runnable; `tools` include `run_block`; read opcode encoding | -| 2 | [Run block](/use/tools/run-block) with a header slot | Per-tx return data equals the slot on Glamsterdam; `slotNumber` rejected on Fusaka | + [Run bytecode](/use/tools/run-bytecode) still uses a mock header (**slot 0**). A chosen beacon slot needs **run block**. -## Fork caveat - +::: warning Fork caveat **Glamsterdam already bundles EIP-7843** in EthereumJS v10. Passing `eips: [7843]` is accepted but is **not** a before/after toggle on Glamsterdam itself. For **7843 off vs on**, run the same program on baseline fork **`fusaka`**, then on preview fork **`glamsterdam`**. Opcode `0x4b` is invalid on Fusaka; on Glamsterdam it pushes `header.slotNumber` (2 gas). You do not need Fusaka for Glamsterdam-only SLOTNUM runs. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-7843 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7843-slotnum-opcode) | -| **Canonical spec** | [EIP-7843](https://github.com/ethereum/EIPs/blob/c3bfd4ba41cf0fcbfe8c404f33ba89f5174971e0/EIPS/eip-7843.md) | -| **Spec date** | 2026-01-20 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**How are ModExp gas costs changing with Fusaka?** EIP-7883 recalibrates the ModExp precompile (`0x05`) gas formula so cost better reflects real work — especially for larger exponents. EIP-7823 (same fork) tightens input bounds. The new formula is **live on Fusaka** (current mainnet EL). - -The MCP server runs **caller-supplied bytecode** that invokes ModExp (typically a `CALL` to `0x05` with the standard ABI: three 32-byte length headers plus `B`, `E`, `M` bodies). Run on **Fusaka** to price today’s mainnet; compare **Pectra** vs **Fusaka** to see the repricing — or sweep input sizes on Fusaka alone to understand the curve. - -## What you can ask your agent + -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +EIP-7883 recalibrates the ModExp precompile (`0x05`) gas formula so cost better reflects real work — especially for larger exponents. EIP-7823 (same fork) tightens input bounds. The new formula is **live on Fusaka** (current mainnet EL). -- *“How much gas does ModExp use for this 32-byte RSA-style exponent on current mainnet EL (Fusaka)?”* -- *“Compare the same B/E/M payload on Pectra vs Fusaka — what did the gas jump?”* -- *“Run ModExp with these B/E/M hex fields on Fusaka and tell me if EIP-7823 rejects the input.”* -- *“Compare ModExp gas for my airdrop verification input on pre-Fusaka and post-Fusaka rules.”* +The MCP server runs **caller-supplied bytecode** that invokes ModExp (typically a `CALL` to `0x05` with the standard ABI: three 32-byte length headers plus `B`, `E`, `M` bodies). Run on **Fusaka** to price today’s mainnet; compare **Pectra** vs **Fusaka** to see the repricing — or sweep input sizes on Fusaka alone to understand the curve. -These are **inspiration prompts** — play, understand, or check *your* inputs. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. Play, understand, or check *your* inputs. + + ## What the server does -| Step | Action | Purpose | -| --- | --- | --- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 7883 catalog entry; read ModExp input layout | -| 2 | [Run bytecode](/use/tools/run-bytecode) | Execute CALL bytecode on `fusaka` (or `pectra` then `fusaka` for compare) | - -## Fork caveat + +::: tip Fork caveat EIP-7883 **introduced at Fusaka** (current mainnet). For **before/after**, run the same bytecode on **`pectra`** (predecessor), then **`fusaka`**, and diff `gasUsed`. The probe derives this pair from `eipIntroductions`. A Fusaka-only run is enough when you only need today’s ModExp cost. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-7883 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7883-modexp-gas-cost-increase) | -| **Canonical spec** | [EIP-7883](https://eips.ethereum.org/EIPS/eip-7883) | -| **Fork page** | [Current mainnet — Fusaka](/use/forks/fusaka) | -| **EIP catalogue** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**What does the block commit to besides state root?** EIP-7928 adds a **block-level access list (BAL)**: every balance, nonce, code, and storage touch during block execution, hashed as **`blockAccessListHash`** in the header. + + +EIP-7928 adds a **block-level access list (BAL)**: every balance, nonce, code, and storage touch during block execution, hashed as **`blockAccessListHash`** in the header. Use **`generate_artifact`** with the same inputs as [Run block](/use/tools/run-block) (1–8 impersonated txs, optional accounts, optional header) on **Glamsterdam** to derive BAL JSON and `hash`. Use **`inspect_artifact`** on a BAL you already have for encoding, canonical order, item cap, and optional hash match — layers A–C, not consensus replay against archive mainnet state. -## What to ask your agent +## Try asking your agent -- *“Generate a BAL for a block with one plain ETH transfer — what does the access list contain?”* -- *“Run the EIP-7928 curriculum step: SSTORE write — show me the BAL JSON and hash.”* -- *“Inspect this BAL JSON — is it well formed and does the hash match `0x…`?”* -- *“Two transfers in one lab block — show blockAccessIndex on each change.”* +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. The [website exploration](https://feelyourprotocol.org/eip-7928-block-level-access-lists) remains the interactive textbook; MCP is the headless twin. -The [website exploration](https://feelyourprotocol.org/eip-7928-block-level-access-lists) remains the interactive textbook; MCP is the headless twin. + ## What the server does -| Step | Action | Purpose | -| --- | --- | --- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm EIP-7928 module, `inspectKinds`, Glamsterdam | -| 2 | **`generate_artifact`** | Derive BAL from your lab block | -| 3 | **`inspect_artifact`** (optional) | Validate caller-supplied BAL JSON or RLP hex | + -**Limits:** Glamsterdam only for generate_artifact; 1–8 txs; empty-ish world unless you prefund accounts; no claim that a BAL came from mainnet block *N* without parent state. +::: warning Limits +Glamsterdam only for `generate_artifact`; 1–8 txs; empty-ish world unless you prefund accounts; no claim that a BAL came from mainnet block *N* without parent state. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** | [EIP-7928 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7928-block-level-access-lists) | -| **Canonical spec** | [EIP-7928](https://github.com/ethereum/EIPs/blob/6c666b8d646df8ea6dcef9638de7b48e3c45ab96/EIPS/eip-7928.md) | -| **Spec date** | 2026-07-09 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**Why add a secp256r1 precompile?** EIP-7951 exposes P-256 (the curve behind Apple/Android passkeys and WebAuthn) at address **`0x100`**. Contracts can verify device-native signatures on-chain. The precompile is **live on Fusaka** (current mainnet EL). - -The MCP server runs **caller-supplied bytecode** that `CALL`s the precompile with five 32-byte fields: message hash, `r`, `s`, public key `x`, public key `y`. A valid signature returns **`0x01`** (32 bytes). Invalid payloads fail or return empty data — your agent can explore both. There is no dedicated precompile input tool; construct a `CALL` to `0x100`. - -## What you can ask your agent + -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +EIP-7951 exposes P-256 (the curve behind Apple/Android passkeys and WebAuthn) at address **`0x100`**. Contracts can verify device-native signatures on-chain. The precompile is **live on Fusaka** (current mainnet EL). -- *“Verify this P-256 / WebAuthn payload on current mainnet EL (Fusaka) — does 0x100 return 0x01?”* -- *“I'm adding passkey verification to a wallet — run my test vector on Fusaka and explain a valid vs invalid return.”* -- *“Help me encode hash, r, s, pubX, pubY for the 0x100 precompile, then run the CALL.”* -- *“What gas does this secp256r1 verification cost on Fusaka?”* +The MCP server runs **caller-supplied bytecode** that `CALL`s the precompile with five 32-byte fields: message hash, `r`, `s`, public key `x`, public key `y`. A valid signature returns **`0x01`** (32 bytes). Invalid payloads fail or return empty data — your agent can explore both. There is no dedicated precompile input tool; construct a `CALL` to `0x100`. -These are **inspiration prompts** — curiosity, understanding, or builder checks. Fusaka is the run target; you do not need a preview fork for 7951. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. Fusaka is the run target; you do not need a preview fork for 7951. + + ## What the server does -| Step | Action | Purpose | -| --- | --- | --- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 7951 catalog entry; read input layout | -| 2 | [Run bytecode](/use/tools/run-bytecode) | Execute CALL bytecode on **`fusaka`** | - -## Fork caveat + +::: tip Fork caveat Available on **Fusaka** (mainnet) and later lineage forks. Teaching is **valid vs invalid** return data, not a gas delta vs Pectra. The probe may list a Pectra→Fusaka pair (activation); that is “precompile missing vs present,” not a pricing lesson. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-7951 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7951-secp256r1-precompile) | -| **Canonical spec** | [EIP-7951](https://eips.ethereum.org/EIPS/eip-7951) | -| **Fork page** | [Current mainnet — Fusaka](/use/forks/fusaka) | -| **EIP catalogue** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**How much contract code can you deploy before the protocol rejects it?** EIP-7954 raises the deployed runtime-code limit from **24 KiB to 64 KiB** and the separate initcode limit from **48 KiB to 128 KiB**. Existing contracts do not change. New deployments get more room, but both boundaries remain consensus validity checks. - -The MCP twin runs caller-supplied contract-creation transactions on **Fusaka** and **Glamsterdam**, then reports whether deployment succeeded, the created address, and how much runtime code was installed. + -## What you can ask your agent +EIP-7954 raises the deployed runtime-code limit from **24 KiB to 64 KiB** and the separate initcode limit from **48 KiB to 128 KiB**. Existing contracts do not change. New deployments get more room, but both boundaries remain consensus validity checks. -- _“Would this 24,577-byte contract deploy on Fusaka and Glamsterdam?”_ -- _“Deploy this initcode at the exact EIP-7954 runtime-code ceiling.”_ -- _“Run the same deployment one byte above the new limit — how does it fail?”_ -- _“Is my deployment blocked by initcode size or deployed runtime-code size?”_ +The MCP twin runs caller-supplied contract-creation transactions on **Fusaka** and **Glamsterdam**, then reports whether deployment succeeded, the created address, and how much runtime code was installed. -These are inspiration prompts. The caller supplies the initcode; the MCP catalogue does not ship demo contracts. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. You supply the initcode; the MCP catalogue does not ship demo contracts. + + ## What the server does -1. [Discover support](/use/tools/describe-capabilities) — read the EIP-7954 limits, comparison forks, and transaction shape. -2. [Run transaction](/use/tools/run-transaction) — omit `to` and pass caller-supplied initcode in `data`. -3. Repeat on `fusaka` and `glamsterdam`; compare `success`, `error`, `createdAddress`, and `deployedCodeSize`. - -## Fork caveat + +::: warning Fork caveat Glamsterdam is the combined-upgrade name; `amsterdam` is its execution-layer alias. The current spec limits are **65,536 bytes** of runtime code and **131,072 bytes** of initcode. Earlier discussion used smaller values, so use the current EIP rather than the historical thread title. Large Glamsterdam deployments also pay EIP-8037 state gas. A 24,577-byte runtime needs about **37.8M** transaction gas and the 65,536-byte ceiling about **100.5M**; the transaction lab ceiling is **110M**. Fusaka keeps its **16,777,216** EIP-7825 transaction cap, so a runtime-code comparison uses a sufficient fork-specific gas limit rather than identical gas limits. +::: ## Twins and spec -- [Website exploration](https://feelyourprotocol.org/eip-7954-contract-size-limit) -- [Canonical EIP-7954 specification](https://github.com/ethereum/EIPs/blob/1dc9bc870f864d7ad1095fc73ba8ca098d02c732/EIPS/eip-7954.md) -- Spec date: 2026-05-21 -- [Test release tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) -- [MCP coverage index](/use/coverage) - -## Changelog + - ## What became possible -**When does calldata cost 64 gas per byte, and when does a normal call still pay 4 and 16?** On Glamsterdam a transaction that mostly carries data pays 64 gas for every calldata byte, zero or not. A call that does enough work keeps the ordinary price: 4 gas per zero byte and 16 per nonzero byte. Fusaka is the same rule at 10 per zero byte and 40 per nonzero byte. Zero bytes are the ones that jump. + + +On Glamsterdam a transaction that mostly carries data pays 64 gas for every calldata byte, zero or not. A call that does enough work keeps the ordinary price: 4 gas per zero byte and 16 per nonzero byte. Fusaka is the same rule at 10 per zero byte and 40 per nonzero byte. Zero bytes are the ones that jump. The MCP server runs **your** transaction. It does not ship demo transactions. Access-list bytes pay that same 64 gas rate (EIP-7981). That rule is already on when you run under Glamsterdam. Pass **`accessList`** on this verb; leave `7981` out of `eips`. -## What you can ask your agent +## Try asking your agent -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. They are starting points — curiosity, understanding, or builder checks. -- _“This calldata is mostly zeros. What does it cost on Glamsterdam versus Fusaka?”_ -- _“This call does real work. Does it still pay 4 and 16 per byte?”_ -- _“My gas limit covers execution but not the floor. Is the transaction invalid before it runs?”_ -- _“An empty calldata call to an existing account — does the floor add anything?”_ - -These are **inspiration prompts** — curiosity, understanding, or builder checks. + ## What the server does -| Step | Action | Purpose | -| ---- | ------ | ------- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm the 7976 row; `tools` is `run_transaction` | -| 2 | [Run transaction](/use/tools/run-transaction) | Same calldata on **`fusaka`** then **`glamsterdam`**; read `gasUsed` | + Prefund the recipient when it should already exist, so the run does not also create an account. `gasUsed` is what the sender pays, including the transaction base. @@ -53,23 +112,23 @@ Empty calldata, one listed address, one storage key — same shape as the explor On Glamsterdam expect **`gasUsed: "23228"`**; on **fusaka** the same payload is **`25300`** (no floor on list bytes). -## Fork caveat - +::: warning Fork caveat Run the **same transaction twice** — **`fusaka`** (baseline), then **`glamsterdam`** (preview). One hundred zero bytes and no other work: **22,000** on Fusaka and **21,400** on Glamsterdam (the per-byte floor rose, and the transaction base moved under EIP-2780). One hundred nonzero bytes: **25,000** on Fusaka and **21,400** on Glamsterdam. An empty call to an existing account stays **15,000** on Glamsterdam — the floor adds nothing. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-7976 on feelyourprotocol.org](https://feelyourprotocol.org/eip-7976-calldata-floor-cost) | -| **Canonical spec** | [EIP-7976](https://github.com/ethereum/EIPs/blob/c998ef94eb16a6af8a9b8e2084f947b17ea14865/EIPS/eip-7976.md) | -| **Spec date** | 2026-07-07 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible + + On **Glamsterdam**, the EVM executes three new stack opcodes: **DUPN** (`0xe6`), **SWAPN** (`0xe7`), and **EXCHANGE** (`0xe8`). They extend deep stack access beyond `DUP16` / `SWAP16` without breaking backward compatibility. The MCP server runs **any** raw bytecode you (or your agent) supply under Glamsterdam and returns deterministic gas, stack, and optional traces. It does not ship fixed demo programs — your agent constructs bytecode from the spec. -## What you can ask your agent - -Once the public MCP is live ([Connect](/use/connect)), try prompts like: - -- *“Is EIP-8024 supported on Glamsterdam? What can I do with it?”* -- *“Give me a minimal DUPN example for depth 17, encode it as bytecode, and run it on Glamsterdam with trace.”* -- *“I have this pre-Glamsterdam stack-manipulation bytecode — rewrite it using SWAPN where it helps, then compare gas before and after.”* -- *“Run this bytecode on Glamsterdam and tell me if DUPN succeeds or fails and why.”* -- *“Compare two variants: my original bytecode vs a DUPN-based rewrite on Glamsterdam.”* - -These are **inspiration prompts** — ask in plain language; your agent uses the connected server and reports results back to you. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. Ask in plain language; your agent uses the connected server and reports results back to you. + + ## What the server does -| Step | Action | Purpose | -| --- | --- | --- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 8024 is runnable; read opcode encoding from the live catalog | -| 2 | [Run bytecode](/use/tools/run-bytecode) | Execute programs under a fork config (Glamsterdam only is fine) | - -## Fork caveat + +::: warning Fork caveat **Glamsterdam already bundles EIP-8024** in EthereumJS v10. Passing `eips: [8024]` is accepted but is **not** a before/after toggle on Glamsterdam itself. For **8024 off vs on** (optional), run the same bytecode on baseline fork **`fusaka`**, then on preview fork **`glamsterdam`**. The catalog exposes this pair on the EIP-8024 module as `comparison: { baselineForkId: "fusaka", previewForkId: "glamsterdam" }`. You do not need osaka for Glamsterdam-only 8024 runs. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-8024 on feelyourprotocol.org](https://feelyourprotocol.org/eip-8024-stack-opcodes-dupn-swapn-exchange) | -| **Canonical spec** | [EIP-8024](https://github.com/ethereum/EIPs/blob/34b49095ca5f7343045da279f04e7ecd1e451393/EIPS/eip-8024.md) | -| **Spec date** | 2026-06-10 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**Why isn't 21,000 gas enough for a simple ETH transfer anymore?** EIP-8037 splits gas into an execution dimension and a state dimension. Creating a new account (first-touch value transfer) charges state-creation gas — about 183,600 (`120 × 1530`) — on top of familiar execution gas. A first write to an empty storage slot charges about 97,920 (`64 × 1530`). - -The MCP server runs a **value-bearing transaction** (`run_transaction`) and returns **paid `gasUsed`** (`gasUsedScope: transaction`). On Glamsterdam it also returns **`txRegularGas`** / **`txStateGas`**. Compare **Fusaka** vs **Glamsterdam** — preview includes state gas; baseline does not. A first-touch transfer is about **21,000 vs 204,600**. - -## What you can ask your agent + -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +EIP-8037 splits gas into an execution dimension and a state dimension. Creating a new account (first-touch value transfer) charges state-creation gas — about 183,600 (`120 × 1530`) — on top of familiar execution gas. A first write to an empty storage slot charges about 97,920 (`64 × 1530`). -- _“Run a 1 wei transfer to an empty account on Glamsterdam — what gas would a wallet need?”_ -- _“Same transfer on Fusaka vs Glamsterdam — how does paid gas differ?”_ -- _“Why would a wallet that still uses a gas limit of 21000 fail a first-touch transfer after Glamsterdam?”_ -- _“If the recipient already has a balance, does Glamsterdam still add state-creation gas?”_ -- _“My contract writes a new storage slot on Glamsterdam — how much extra paid gas vs Fusaka?”_ +The MCP server runs a **value-bearing transaction** (`run_transaction`) and returns **paid `gasUsed`** (`gasUsedScope: transaction`). On Glamsterdam it also returns **`txRegularGas`** / **`txStateGas`**. Compare **Fusaka** vs **Glamsterdam** — preview includes state gas; baseline does not. A first-touch transfer is about **21,000 vs 204,600**. -These are **inspiration prompts** — curiosity, understanding, or builder checks. +## Try asking your agent + +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. They are starting points — curiosity, understanding, or builder checks. + + ## What the server does -| Step | Action | Purpose | -| ---- | ------ | ------- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 8037 catalog entry; `tools` include `run_transaction` | -| 2 | [Run transaction](/use/tools/run-transaction) | Execute a value tx on **`glamsterdam`** or **`fusaka`**; read `gasUsed` and `txStateGas` | + ```json { @@ -40,23 +107,23 @@ These are **inspiration prompts** — curiosity, understanding, or builder check SSTORE programs may also use [Run bytecode](/use/tools/run-bytecode). -## Fork caveat - +::: warning Fork caveat Run the **same transaction twice** — **`fusaka`** (baseline), then **`glamsterdam`** (preview). Lead with **`gasUsed`** (paid tx gas): about **21,000 vs 204,600** for first-touch. On Glamsterdam, `txStateGas` is the state dimension (~183,600). +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-8037 on feelyourprotocol.org](https://feelyourprotocol.org/eip-8037-state-creation-gas) | -| **Canonical spec** | [EIP-8037](https://github.com/ethereum/EIPs/blob/5a8c80897aeb0952322cd0dfff767c541002b8c3/EIPS/eip-8037.md) | -| **Spec date** | 2026-07-31 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - ## What became possible -**When you SSTORE on Glamsterdam, what are you paying for — touching the slot, changing it, or creating it?** EIP-8038 splits state-touching costs into **touch** (load), **change** (write), and **create** (new leaf). Cold storage access stays 2,100. The write surcharge on an existing slot is `STORAGE_WRITE` 10,000 (was about 2,800). Creating a slot is still EIP-8037 state gas (about 97,920). `EXTCODESIZE` / `EXTCODECOPY` pay an extra warm read. + + +EIP-8038 splits state-touching costs into **touch** (load), **change** (write), and **create** (new leaf). Cold storage access stays 2,100. The write surcharge on an existing slot is `STORAGE_WRITE` 10,000 (was about 2,800). Creating a slot is still EIP-8037 state gas (about 97,920). `EXTCODESIZE` / `EXTCODECOPY` pay an extra warm read. The MCP server runs **your** program. It does not ship demo bytecode. @@ -12,25 +22,85 @@ The MCP server runs **your** program. It does not ship demo bytecode. - **SSTORE write (change):** [Run bytecode](/use/tools/run-bytecode). Existing-slot cold write is about **5,006 vs 12,106**. Seed `accounts[].storage` on `0x00000000000000000000000000000000000000b1`. - **SSTORE create vs change (paid / state gas):** [Run transaction](/use/tools/run-transaction) with `code` at `to`. On Glamsterdam, `txStateGas` is 0 for an existing slot and about 97,920 for a new one. On bytecode, new-slot Glamsterdam sets `stateGasSpilled` 97,920 (`gasUsed` includes the spill). -## What you can ask your agent - -Once the public MCP is live ([Connect](/use/connect)), try prompts like: +## Try asking your agent -- _“Did cold SLOAD get more expensive on Glamsterdam?”_ -- _“Why is EXTCODESIZE more expensive than BALANCE after Glamsterdam?”_ -- _“SSTORE into a slot that already holds a value — Fusaka vs Glamsterdam, and is there state gas?”_ -- _“First write to an empty slot on Glamsterdam — how much is regular vs state gas?”_ -- _“If I SSTORE the same value twice, do I pay STORAGE_WRITE twice?”_ +Once the public MCP is live ([Connect](/use/connect)), copy a prompt into your agent. They are starting points — curiosity, understanding, or builder checks. -These are **inspiration prompts** — curiosity, understanding, or builder checks. + ## What the server does -| Step | Action | Purpose | -| ---- | ------ | ------- | -| 1 | [Discover support](/use/tools/describe-capabilities) | Confirm 8038 catalog row; `tools` include `run_bytecode` and `run_transaction` | -| 2 | [Run bytecode](/use/tools/run-bytecode) | SLOAD / EXTCODESIZE / SSTORE program gas on **`fusaka`** then **`glamsterdam`**; read `gasUsed` (and `stateGasSpilled` on a new slot) | -| 3 | [Run transaction](/use/tools/run-transaction) | Paid SSTORE `code` at `to` when you need `txStateGas`; seed `accounts[].storage` for an existing slot | + Seed storage (existing slot) on **run_bytecode** — put slots on the lab execution account: @@ -49,25 +119,25 @@ Seed storage (existing slot) on **run_bytecode** — put slots on the lab execut For paid `txStateGas`, use **run_transaction** with `code` at `to` and `accounts[].storage` on that `to`. Omit `storage` for a new empty slot. Repeat with `"baseHardfork": "fusaka"`. -## Fork caveat - +::: warning Fork caveat Run the **same program twice** — **`fusaka`** (baseline), then **`glamsterdam`** (preview). Glamsterdam already bundles EIP-8038 in EthereumJS v10. Passing `eips: [8038]` is accepted but is not a pre/post toggle. Paid `gasUsed` on a transaction is **not** a clean 5,006 vs 12,106 — it includes 21,000 intrinsic and EIP-2780 recipient access. Lead with call-frame **`gasUsed`** on `run_bytecode` for the write jump, and **`txStateGas`** / **`stateGasSpilled`** for create vs change. Cold SLOAD stays **2,103**. EXTCODESIZE is about **2,603 vs 3,103**. +::: ## Twins and spec -| | Link | -| --- | --- | -| **Website exploration** (interactive textbook) | [EIP-8038 on feelyourprotocol.org](https://feelyourprotocol.org/eip-8038-state-access-gas) | -| **Canonical spec** | [EIP-8038](https://github.com/ethereum/EIPs/blob/8331fb3eed0a5366b28b25a016f1ad04fac0fa8e/EIPS/eip-8038.md) | -| **Spec date** | 2026-08-04 | -| **Test release** | [tests-glamsterdam-devnet@v8.1.0](https://github.com/ethereum/execution-specs/releases/tag/tests-glamsterdam-devnet@v8.1.0) | -| **Coverage index** | [Coverage](/use/coverage) | - -## Changelog + - Pass `{ "baseHardfork": "fusaka", "eips": [] }` (or aliases `osaka`, `mainnet-el`). Fusaka is **not** the default lab fork — Glamsterdam is. @@ -46,11 +55,10 @@ Provenance on a generic Fusaka run lists advertised modules **7883** and **7951* | [EIP catalogue](/use/coverage) | Lineage + EIP twins | | [Fork timeline](https://ethereum.org/ethereum-forks/) | External reference | -## Changelog - - ## Also on Amsterdam -| Topic | Twin | -| --- | --- | -| Contract creation size limit | [EIP-7954](/use/eips/eip-7954) | -| Block access lists (generate / inspect) | [EIP-7928](/use/eips/eip-7928) | -| DUPN / SWAPN / EXCHANGE | [EIP-8024](/use/eips/eip-8024) | -| SLOTNUM opcode / header slot in lab blocks | [EIP-7843](/use/eips/eip-7843) | -| Calldata floor (64 gas per byte when the call does little else) | [EIP-7976](/use/eips/eip-7976) | + ## Compare to mainnet today @@ -45,9 +47,13 @@ When you care about **before vs after**, ask for the same experiment on **Fusaka ## How your agent runs it -1. Connect — [Connect](/use/connect) -2. Ask in plain language — [What you can ask](/use/capabilities) -3. Optional deep dive — per-EIP pages above or the full [EIP catalogue](/use/coverage) + Bundled rule changes that do not have their own exploration still apply on Amsterdam. SELFDESTRUCT no longer burns ETH. Access-list bytes pay the same 64-gas floor as calldata. Your agent uses generic Amsterdam runs without naming those ids. @@ -63,11 +69,10 @@ Consensus-layer and networking EIPs scheduled beside Amsterdam are **not** execu | [Limits](/use/guarantees) | Determinism, BYOS, ceilings | | [EIP-7773](https://eips.ethereum.org/EIPS/eip-7773) | Glamsterdam meta | -## Changelog - - - ## Try these first on Amsterdam After [Connect](/use/connect), paste a prompt — you do not need EIP numbers in conversation. -| Ask your agent | Twin | -| --- | --- | -| *“What intrinsic gas does a simple ETH transfer use under Amsterdam?”* | [EIP-2780](/use/eips/eip-2780) | -| *“This calldata is mostly zeros. What does it cost on Amsterdam versus Fusaka?”* | [EIP-7976](/use/eips/eip-7976) | -| *“Send 1 wei to an empty account on Amsterdam — what gas does the wallet need?”* | [EIP-8037](/use/eips/eip-8037) | -| *“How does SSTORE on an existing slot price on Amsterdam vs Fusaka?”* | [EIP-8038](/use/eips/eip-8038) | -| *“I cleared storage and got a refund. Why does the block still count the full gas?”* | [EIP-7778](/use/eips/eip-7778) | -| *“Does a value transfer emit a receipt log on Amsterdam?”* | [EIP-7708](/use/eips/eip-7708) | + More on the Amsterdam bundle: [deploy limits](/use/eips/eip-7954), [block access lists](/use/eips/eip-7928), [stack opcodes](/use/eips/eip-8024), [SLOTNUM](/use/eips/eip-7843). Already on mainnet here: [ModExp](/use/eips/eip-7883), [P-256](/use/eips/eip-7951) on Fusaka. ## Where to go next -1. **[Connect](/use/connect)** — Cursor, Claude, Codex, and a generic MCP config (steps ready before the URL goes live). -2. **[Amsterdam now](/use/forks/glamsterdam)** — the fork bundle and the questions we highlight first. -3. **[What you can ask](/use/capabilities)** — five jobs this server is built for. + Want to click through a change first? The [website explorations](https://feelyourprotocol.org) are the visual twin of many Amsterdam EIPs — same questions, human UI. -## Changelog - - + ## Purpose Return a machine-readable snapshot of what this server can **actually run**: engine version, ceilings, **named fork capabilities**, **`inspectKinds`**, and **runnable EIP modules**. Each named fork describes a generic hardfork run (summary, keywords, **`tools`**, advertised `relatedEips`) — you do not need to name an EIP. Each EIP module describes **what became possible** (opcodes, encoding rules, keywords, **`tools`**) — not demo programs. Unimplemented EIPs are omitted. Use **`eips[].tools`** / **`namedForks[].tools`** to pick **`run_bytecode`**, **`run_transaction`**, **`run_block`**, **`generate_artifact`**, or **`inspect_artifact`**. **`queryShapes[]`** is the dictionary from internal catalog ids (`simulate`, `generate`, …) to those MCP names — do not call the `id`. @@ -74,9 +84,7 @@ _Output (abbreviated):_ [describe_capabilities.input.json](/schemas/describe_capabilities.input.json) -## Changelog - - + ## Purpose Derive **structured artifacts** from a lab block run — same inputs as [Run Block](/use/tools/run-block) (1–8 txs, accounts, optional header). First kind: **`block-access-list`** (EIP-7928 BAL JSON + hash). @@ -32,9 +42,7 @@ Pair with [Inspect Artifact](/use/tools/inspect-artifact) on caller-supplied BAL [generate_artifact.input.json](/schemas/generate_artifact.input.json) -## Changelog - - + ## Purpose Judge a **caller-supplied** structured artifact **without chain state** — encoding (layer A), canonical structure and item cap (layer B), optional hash match (layer C). Not consensus replay against mainnet. @@ -31,9 +41,7 @@ See [Describe Capabilities](/use/tools/describe-capabilities) for `inspectKinds` [inspect_artifact.input.json](/schemas/inspect_artifact.input.json) -## Changelog - - + ## Purpose Run **1–8 impersonated transactions as one lab block** and receive a **header snapshot** plus **per-tx receipts**. Senders are impersonated from each `from` — no private key. @@ -75,9 +85,7 @@ Expected: `success: true`, `transactions[0].gasUsed: "204600"`, `txStateGas: "18 Max **8** transactions. See [Guarantees](/use/guarantees) for gas ceilings. -## Changelog - - + ## Purpose Run **caller-supplied** raw EVM bytecode under a chosen fork / EIP configuration and receive a structured result — call-frame gas used, return data, final stack, optional opcode trace, and provenance. @@ -140,9 +150,7 @@ Deep stack + `DUPN` — invalid on fusaka baseline; valid on Glamsterdam preview See [Guarantees](/use/guarantees) for ceilings (max gas, bytecode size, trace steps). -## Changelog - - + ## Purpose Run a **message-call or contract-creation transaction** under a chosen fork and receive **paid transaction gas** plus receipt logs. The sender is impersonated from `from` — no private key. @@ -83,9 +93,7 @@ Expected: `success: true`, `gasUsed: "204600"`, `txStateGas: "183600"`. The same See [Guarantees](/use/guarantees) for gas ceilings. The higher transaction-only ceiling supports Glamsterdam's EIP-8037 state-gas reservoir for large deployments. -## Changelog - -Terms', }, }, }) diff --git a/mcp-docs/__tests__/urlStability.spec.ts b/mcp-docs/__tests__/urlStability.spec.ts index 11ccdaf1..e8911d3c 100644 --- a/mcp-docs/__tests__/urlStability.spec.ts +++ b/mcp-docs/__tests__/urlStability.spec.ts @@ -38,6 +38,7 @@ describe('mcp-docs URL stability', () => { '/use/forks/glamsterdam', '/use/forks/fusaka', '/use/tools/run-transaction', + '/use/terms', ]) { expect(pages).toContain(required) } @@ -52,6 +53,13 @@ describe('mcp-docs URL stability', () => { } }) + it('links Terms from the sidebar, the footer, and Connect', () => { + const config = readFileSync(join(ROOT, '.vitepress/config.ts'), 'utf8') + expect(config).toContain("link: '/use/terms'") + expect(config).toContain('/use/terms.html') + expect(readFileSync(join(ROOT, 'use/connect.md'), 'utf8')).toContain('(/use/terms)') + }) + it('every internal Markdown link points at an existing page', () => { const broken: string[] = [] for (const file of markdownFiles(ROOT)) { diff --git a/mcp-docs/use/connect.md b/mcp-docs/use/connect.md index dd68d914..3d016106 100644 --- a/mcp-docs/use/connect.md +++ b/mcp-docs/use/connect.md @@ -16,7 +16,7 @@ One URL, no wallet, no API key. Add it to your agent, then ask a question in pla ]" /> -For browser learning, use **[feelyourprotocol.org](https://feelyourprotocol.org)**. +For browser learning, use **[feelyourprotocol.org](https://feelyourprotocol.org)**. Using the hosted server means you accept the short [Terms](/use/terms). ## Add the server to your agent diff --git a/mcp-docs/use/terms.md b/mcp-docs/use/terms.md new file mode 100644 index 00000000..bf2f1987 --- /dev/null +++ b/mcp-docs/use/terms.md @@ -0,0 +1,46 @@ +# Terms + +*Last updated: 6 October 2026* + +Short version: this is a free lab for protocol experiments. Use it to learn and to check ideas. Verify anything you ship, because we do not guarantee that a result is correct. + +## These terms + +These terms cover the hosted MCP server at `https://mcp.feelyourprotocol.org/mcp` and this documentation. By connecting your agent or client to the server, you accept them. The server is operated by Holger Drewes (see the [imprint](https://feelyourprotocol.org/imprint)). The [explorations website](https://feelyourprotocol.org) is a separate offer. + +There is no account, and the lab is not an analytics product. + +## What you get + +A deterministic lab run under a named Ethereum fork, using EthereumJS. The server does not read mainnet or any other chain, and it is not legal, financial, or security advice. See [Limits](/use/guarantees) for what it runs and what it does not. + +## What you send + +You send bytecode, transactions, and demo accounts in each call. Do not send secrets, private keys, or anything you cannot share. Do not flood the server or use it to attack anyone. + +The server is free at launch. We may change, pause, or stop it. If a paid tier arrives (see [Pricing](/use/pricing)), these terms will be updated before anyone is charged. + +## No warranty + +The service is provided as is and as available. We build the lab with ordinary care: tests, pinned spec snapshots, and reviewed changes. That care does not guarantee a result is correct. + +A result can be wrong or incomplete, and it can differ from another client or a later version of the spec. It applies to the inputs and the fork you sent. The same inputs give the same answer again, which is not a promise that the answer is the one to ship. + +## Liability + +While the server is free, we are liable only for intent and gross negligence, as far as the law allows. Harm to life, body, or health is never excluded. + +We are not liable for indirect or consequential loss from using a result. That includes lost funds, a wrong gas limit, a failed deploy, an audit conclusion, or what an agent tells you. + +Decisions stay with you, including the ones your agent makes on your behalf. Check anything you rely on. + +## Changes and law + +We may update this page. The date at the top is the version. German law applies, as far as the law lets us choose it. Questions go through the [imprint](https://feelyourprotocol.org/imprint). + + From a24c0df12ac154009a96cf7a3f1515cbf1352f75 Mon Sep 17 00:00:00 2001 From: Holger Drewes <931137+holgerd77@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:38:53 +0200 Subject: [PATCH 3/4] Design principles note the stateless HTTP transport Co-authored-by: Cursor --- mcp-docs/internals/design-principles.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mcp-docs/internals/design-principles.md b/mcp-docs/internals/design-principles.md index 142cb699..5219138b 100644 --- a/mcp-docs/internals/design-principles.md +++ b/mcp-docs/internals/design-principles.md @@ -6,7 +6,7 @@ These boundaries apply to everything we ship. - **Isolated lab, not a node (BYOS)** — We never attach to mainnet, an L2, or an archive RPC. The caller brings — or we **construct in this request** — the accounts, code, and storage the run needs. That is what “stateless” meant: not a full node, **not** “the lab world must be empty.” - **Demand-built prestate is in scope** — Prefund, install code, seed storage, several txs in one `run_block`. “Pre-deploy this contract, then EXTCODESIZE it” is one call. Default after the call: discard that world (cheap, parallelizable). -- **Call isolation is the default** — Each tool call is a fresh lab from **that payload**. An MCP transport session (stdio process, HTTP `mcp-session-id`) is not an EVM. Continuation across prompts (“use the contract we just deployed”) is optional later (caller-held snapshot or a short-lived gateway handle) — not shipped, not implied. +- **Call isolation is the default** — Each tool call is a fresh lab from **that payload**. The hosted HTTP transport keeps no session: every POST stands alone, and a gateway restart does not drop an install. Continuation across prompts (“use the contract we just deployed”) is optional later (caller-held snapshot or a short-lived gateway handle) — not shipped, not implied. - **Raw bytecode, base-layer only** — No Solidity compilation in the service. ERC/application-layer concerns are out of scope. - **Observability first** — Rich execution traces (stack, memory, gas, opcodes) are a primary deliverable. - **Intent-driven MCP tools** — Tools match use cases (run bytecode, run transaction, run block, generate_artifact, inspect_artifact, …), not raw library APIs one-to-one. @@ -26,6 +26,7 @@ End-user summary: [Capabilities](/use/capabilities), [Guarantees](/use/guarantee