Clients use resource URIs as stable identifiers to fetch, cache, and subscribe to server data. A bare string like config or logs is not a URI — it breaks RFC 6570 template parsing and prevents clients from constructing valid resource reads. Without mimeType, clients cannot determine whether to render content as markdown, JSON, or binary, which causes display failures or silently mangled output. These are fundamental contract violations that break resource discovery for any client following the MCP spec.
High because invalid URI templates and missing mimeType fields cause resource reads to fail at the protocol level, making all registered resources inaccessible to compliant clients.
Replace bare strings with proper URI templates (RFC 6570) and always set mimeType in the resource options.
// src/resources/index.ts
server.resource(
'app-config',
'config://app/settings',
{ mimeType: 'application/json', description: 'Current application configuration as JSON' },
async (uri) => ({
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(config) }]
})
)
// Parameterized resource — descriptive template variable
server.resource(
'log-file',
new ResourceTemplate('logs://app/{date}', { list: undefined }),
{ mimeType: 'text/plain', description: 'Application log for a date in YYYY-MM-DD format' },
async (uri, { date }) => ({
contents: [{ uri: uri.href, mimeType: 'text/plain', text: await readLog(date) }]
})
)
ID: mcp-server.tool-definitions.resource-uris
Severity: high
What to look for: Count all resource URI templates. Enumerate which follow URI template syntax (RFC 6570) vs. which use non-standard patterns. Examine all resource registrations (server.resource() or resource handler definitions). Check that each resource has a valid URI or URI template (RFC 6570). Common patterns: file:///path, db://table/row, custom://resource-type/id. Check that mimeType is set for each resource (e.g., text/plain, application/json, text/markdown). Check for resource templates that use {param} placeholders with corresponding descriptions.
Pass criteria: Every resource has a valid URI or URI template. The mimeType is set and matches the actual content type. URI templates have descriptive parameter names. 100% of resource URIs must use valid URI template syntax.
Fail criteria: Resources with invalid URIs, missing mimeType, or URI templates with unnamed/undescribed parameters.
Skip (N/A) when: The server registers no resources (tool-only server). All checks skip when no MCP server is detected.
Cross-reference: For resource content types, see return-content-types.
Detail on fail: "Resource 'config' uses URI 'config' instead of a proper URI like 'config://app/settings'. Resource 'logs' has no mimeType — clients won't know how to render the content" or "Resource template 'db://{x}' uses opaque parameter name 'x' instead of descriptive 'table_name'"
Remediation: Resources are how clients discover and read your server's data. Use proper URIs and set content types:
// src/resources/index.ts — proper URI templates
{ uri: "file:///{path}", name: "File contents" } // Valid RFC 6570 template
server.resource(
'app-config',
'config://app/settings',
{ mimeType: 'application/json', description: 'Current application configuration' },
async (uri) => ({
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(config) }]
})
)
// Resource template with descriptive parameters
server.resource(
'log-file',
new ResourceTemplate('logs://app/{date}', { list: undefined }),
{ mimeType: 'text/plain', description: 'Application log file for a specific date' },
async (uri, { date }) => ({
contents: [{ uri: uri.href, mimeType: 'text/plain', text: await readLog(date) }]
})
)