Skip to main content
Glama

MCP Auth

by rubenpenap
tools.ts12.1 kB
import { createEntryInputSchema, createTagInputSchema, entryIdSchema, entryListItemSchema, entryTagIdSchema, entryTagSchema, entryWithTagsSchema, tagIdSchema, tagListItemSchema, tagSchema, updateEntryInputSchema, updateTagInputSchema, userSchema, } from '@epic-web/epicme-db-client/schema' import { invariant } from '@epic-web/invariant' import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js' import { z } from 'zod' import { type EpicMeMCP } from './index.ts' import { suggestTagsSampling } from './sampling.ts' export async function initializeTools(agent: EpicMeMCP) { if (agent.hasScope('user:read')) { agent.server.registerTool( 'whoami', { title: 'Who Am I', description: 'Get information about the currently logged in user', annotations: { readOnlyHint: true, openWorldHint: false, }, outputSchema: { user: userSchema, scopes: z.array(z.string()) }, }, async () => { const user = await agent.requireUser() const structuredContent = { user, scopes: agent.requireAuthInfo().scopes, } return { structuredContent, content: [createText(structuredContent)], } }, ) } if (agent.hasScope('entries:write')) { agent.server.registerTool( 'create_entry', { title: 'Create Entry', description: 'Create a new journal entry', annotations: { destructiveHint: false, openWorldHint: false, }, inputSchema: createEntryInputSchema, outputSchema: { entry: entryWithTagsSchema }, }, async (entry) => { const createdEntry = await agent.db.createEntry(entry) if (entry.tags) { for (const tagId of entry.tags) { await agent.db.addTagToEntry({ entryId: createdEntry.id, tagId, }) } } void suggestTagsSampling(agent, createdEntry.id) const structuredContent = { entry: createdEntry } return { structuredContent, content: [ createText( `Entry "${createdEntry.title}" created successfully with ID "${createdEntry.id}"`, ), createEntryResourceLink(createdEntry), createText(structuredContent), ], } }, ) } if (agent.hasScope('entries:read')) { agent.server.registerTool( 'get_entry', { title: 'Get Entry', description: 'Get a journal entry by ID', annotations: { readOnlyHint: true, openWorldHint: false, }, inputSchema: entryIdSchema, outputSchema: { entry: entryWithTagsSchema }, }, async ({ id }) => { const entry = await agent.db.getEntry(id) invariant(entry, `Entry with ID "${id}" not found`) const structuredContent = { entry } return { structuredContent, content: [ createEntryResourceLink(entry), createText(structuredContent), ], } }, ) agent.server.registerTool( 'list_entries', { title: 'List Entries', description: 'List all journal entries', annotations: { readOnlyHint: true, openWorldHint: false, }, outputSchema: { entries: z.array(entryListItemSchema) }, }, async () => { const entries = await agent.db.getEntries() const entryLinks = entries.map(createEntryResourceLink) const structuredContent = { entries } return { structuredContent, content: [ createText(`Found ${entries.length} entries.`), ...entryLinks, createText(structuredContent), ], } }, ) } if (agent.hasScope('entries:write')) { agent.server.registerTool( 'update_entry', { title: 'Update Entry', description: 'Update a journal entry. Fields that are not provided (or set to undefined) will not be updated. Fields that are set to null or any other value will be updated.', annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false, }, inputSchema: updateEntryInputSchema, outputSchema: { entry: entryWithTagsSchema }, }, async ({ id, ...updates }) => { const existingEntry = await agent.db.getEntry(id) invariant(existingEntry, `Entry with ID "${id}" not found`) const updatedEntry = await agent.db.updateEntry(id, updates) const structuredContent = { entry: updatedEntry } return { structuredContent, content: [ createText( `Entry "${updatedEntry.title}" (ID: ${id}) updated successfully`, ), createEntryResourceLink(updatedEntry), createText(structuredContent), ], } }, ) agent.server.registerTool( 'delete_entry', { title: 'Delete Entry', description: 'Delete a journal entry', annotations: { idempotentHint: true, openWorldHint: false, }, inputSchema: entryIdSchema, outputSchema: { success: z.boolean(), entry: entryWithTagsSchema }, }, async ({ id }) => { const existingEntry = await agent.db.getEntry(id) invariant(existingEntry, `Entry with ID "${id}" not found`) const confirmed = await elicitConfirmation( agent, `Are you sure you want to delete entry "${existingEntry.title}" (ID: ${id})?`, ) if (!confirmed) { const structuredContent = { success: false, entry: existingEntry, } return { structuredContent, content: [ createText( `Deleting entry "${existingEntry.title}" (ID: ${id}) rejected by the user.`, ), createText(structuredContent), ], } } await agent.db.deleteEntry(id) const structuredContent = { success: true, entry: existingEntry } return { structuredContent, content: [ createText( `Entry "${existingEntry.title}" (ID: ${id}) deleted successfully`, ), createEntryResourceLink(existingEntry), createText(structuredContent), ], } }, ) } if (agent.hasScope('tags:write')) { agent.server.registerTool( 'create_tag', { title: 'Create Tag', description: 'Create a new tag', annotations: { destructiveHint: false, openWorldHint: false, }, inputSchema: createTagInputSchema, outputSchema: { tag: tagSchema }, }, async (tag) => { const createdTag = await agent.db.createTag(tag) const structuredContent = { tag: createdTag } return { structuredContent, content: [ createText( `Tag "${createdTag.name}" created successfully with ID "${createdTag.id}"`, ), createTagResourceLink(createdTag), createText(structuredContent), ], } }, ) } if (agent.hasScope('tags:read')) { agent.server.registerTool( 'get_tag', { title: 'Get Tag', description: 'Get a tag by ID', annotations: { readOnlyHint: true, openWorldHint: false, }, inputSchema: tagIdSchema, outputSchema: { tag: tagSchema }, }, async ({ id }) => { const tag = await agent.db.getTag(id) invariant(tag, `Tag ID "${id}" not found`) const structuredContent = { tag } return { structuredContent, content: [createTagResourceLink(tag), createText(structuredContent)], } }, ) agent.server.registerTool( 'list_tags', { title: 'List Tags', description: 'List all tags', annotations: { readOnlyHint: true, openWorldHint: false, }, outputSchema: { tags: z.array(tagListItemSchema) }, }, async () => { const tags = await agent.db.getTags() const tagLinks = tags.map(createTagResourceLink) const structuredContent = { tags } return { structuredContent, content: [ createText(`Found ${tags.length} tags.`), ...tagLinks, createText(structuredContent), ], } }, ) } if (agent.hasScope('tags:write')) { agent.server.registerTool( 'update_tag', { title: 'Update Tag', description: 'Update a tag', annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false, }, inputSchema: updateTagInputSchema, outputSchema: { tag: tagSchema }, }, async ({ id, ...updates }) => { const updatedTag = await agent.db.updateTag(id, updates) const structuredContent = { tag: updatedTag } return { structuredContent, content: [ createText( `Tag "${updatedTag.name}" (ID: ${id}) updated successfully`, ), createTagResourceLink(updatedTag), createText(structuredContent), ], } }, ) agent.server.registerTool( 'delete_tag', { title: 'Delete Tag', description: 'Delete a tag', annotations: { idempotentHint: true, openWorldHint: false, }, inputSchema: tagIdSchema, outputSchema: { success: z.boolean(), tag: tagSchema }, }, async ({ id }) => { const existingTag = await agent.db.getTag(id) invariant(existingTag, `Tag ID "${id}" not found`) const confirmed = await elicitConfirmation( agent, `Are you sure you want to delete tag "${existingTag.name}" (ID: ${id})?`, ) if (!confirmed) { const structuredContent = { success: false, tag: existingTag } return { structuredContent, content: [ createText( `Deleting tag "${existingTag.name}" (ID: ${id}) rejected by the user.`, ), createTagResourceLink(existingTag), createText(structuredContent), ], } } await agent.db.deleteTag(id) const structuredContent = { success: true, tag: existingTag } return { structuredContent, content: [ createText( `Tag "${existingTag.name}" (ID: ${id}) deleted successfully`, ), createTagResourceLink(existingTag), createText(structuredContent), ], } }, ) } if (agent.hasScope('entries:write')) { agent.server.registerTool( 'add_tag_to_entry', { title: 'Add Tag to Entry', description: 'Add a tag to an entry', annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false, }, inputSchema: entryTagIdSchema, outputSchema: { success: z.boolean(), entryTag: entryTagSchema }, }, async ({ entryId, tagId }) => { const tag = await agent.db.getTag(tagId) const entry = await agent.db.getEntry(entryId) invariant(tag, `Tag ${tagId} not found`) invariant(entry, `Entry with ID "${entryId}" not found`) const entryTag = await agent.db.addTagToEntry({ entryId, tagId, }) const structuredContent = { success: true, entryTag } return { structuredContent, content: [ createText( `Tag "${tag.name}" (ID: ${entryTag.tagId}) added to entry "${entry.title}" (ID: ${entryTag.entryId}) successfully`, ), createTagResourceLink(tag), createEntryResourceLink(entry), createText(structuredContent), ], } }, ) } } function createText(text: unknown): CallToolResult['content'][number] { if (typeof text === 'string') { return { type: 'text', text } } else { return { type: 'text', text: JSON.stringify(text) } } } type ResourceLinkContent = Extract< CallToolResult['content'][number], { type: 'resource_link' } > function createEntryResourceLink(entry: { id: number title: string }): ResourceLinkContent { return { type: 'resource_link', uri: `epicme://entries/${entry.id}`, name: entry.title, description: `Journal Entry: "${entry.title}"`, mimeType: 'application/json', } } function createTagResourceLink(tag: { id: number name: string }): ResourceLinkContent { return { type: 'resource_link', uri: `epicme://tags/${tag.id}`, name: tag.name, description: `Tag: "${tag.name}"`, mimeType: 'application/json', } } async function elicitConfirmation(agent: EpicMeMCP, message: string) { const capabilities = agent.server.server.getClientCapabilities() if (!capabilities?.elicitation) { return true } const result = await agent.server.server.elicitInput({ message, requestedSchema: { type: 'object', properties: { confirmed: { type: 'boolean', description: 'Whether to confirm the action', }, }, }, }) return result.action === 'accept' && result.content?.confirmed === true }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rubenpenap/mcp-auth'

If you have feedback or need assistance with the MCP directory API, please join our Discord server